//build/2016 - The Future of C#

Case #1: 擺脫單一回傳值限制:用 C# 7 Tuples 設計多回傳 API

Problem Statement(問題陳述)

  • 業務場景:需要撰寫能一次回傳多個結果的 API(例如同時回傳 Width 與 Height 的 Size、或回傳值與錯誤狀態),但又希望保有強型別與良好可讀性,避免用全域變數、out/ref 參數或例外作為控制流。
  • 技術挑戰:C/早期語言只能回傳單一值,導致像 getchar 必須用 int 回傳以塞入 EOF;在 .NET 常見的替代方案(out/ref、Tuple)則可讀性差或侵入性高。
  • 影響範圍:API 設計混亂、呼叫端使用成本上升、轉型與暫存變數增加、維護困難。
  • 複雜度評級:中

Root Cause Analysis(根因分析)

  • 直接原因:
    1. 語言長期限制為單一回傳值,導致必須「借道」回傳其他資訊。
    2. out/ref 破壞回傳值的直觀語義,且需先宣告變數,提升認知負擔。
    3. Tuple<T1,T2> 的 Item1/Item2 命名不語意化,降低可讀性。
  • 深層原因:
    • 架構層面:API 介面未明確表達「多值」語義。
    • 技術層面:過去缺乏語法層支援的多回傳與元素命名。
    • 流程層面:習慣用 out/ref/Exception 達成流程控制與結果回傳。

Solution Design(解決方案設計)

  • 解決策略:採用 C# 7 的 Tuples(具名元素)做為多回傳的標準介面,讓 API 直接以 (Type1 Name1, Type2 Name2) 表達輸出,提升語意清晰度與型別安全,同時不引入額外類別。

  • 實施步驟:
    1. 介面重構為具名 Tuple 回傳
      • 實作細節:將 GetSize 改為 (int Width, int Height) GetSize()。
      • 所需資源:C# 7 編譯器支援(VS 15 Preview 時期:Tuples 尚可能受限)。
      • 預估時間:0.5 小時/個 API。
    2. 呼叫端改用具名元素存取
      • 實作細節:var s = GetSize(); 使用 s.Width/s.Height。
      • 所需資源:IDE/編譯器。
      • 預估時間:0.5 小時。
    3. 導入程式碼範例與使用準則
      • 實作細節:規範「多值一律用 Tuple 命名元素」。
      • 所需資源:團隊規範文件。
      • 預估時間:1 小時。
  • 關鍵程式碼/設定: ```csharp // 以具名 Tuple 一次回傳多個值 (int Width, int Height) GetSize() { return (Width: 800, Height: 600); }

void Demo() { var s = GetSize(); Console.WriteLine($”Size = ({s.Width}, {s.Height})”); }


- 實際案例:以 Size(Width, Height)為例,將原 out/ref 或自訂 POCO 改為具名 Tuple。
- 實作環境:Visual Studio 15 Preview + C# 7(注意:當時 Tuples/Records 在 VS15 Preview 尚未完整支援)。
- 實測數據:
  - 改善前:out/ref 版約需 8-10 行、需預宣告變數、API 簽章冗長。
  - 改善後:Tuple 版 4-5 行、具名元素語意清晰。
  - 改善幅度:示例中 LoC 減少約 40-60%,可讀性顯著提升;效能無差異(語法糖)。

Learning Points(學習要點)
- 核心知識點:
  - C# 7 具名 Tuples 的宣告與使用。
  - 多回傳的語意化與型別安全。
  - 語法糖對 IL/效能不造成改變。
- 技能要求:
  - 必備技能:C# 方法簽章、基礎泛型/Value 型別觀念。
  - 進階技能:公共 API 設計準則、可讀性與可維護性評估。
- 延伸思考:
  - 應用場景:解析函數、查詢 API、一次性資料分組。
  - 風險:過度使用 Tuple 取代合理的類型抽象,失去可演進性。
  - 優化:為常用資料形狀升級為 Record/POCO。

Practice Exercise(練習題)
- 基礎練習:把兩個 out 參數的 API 改成具名 Tuple 回傳(30 分鐘)。
- 進階練習:設計可回傳 3-4 個相關值的服務方法並撰寫單元測試(2 小時)。
- 專案練習:重構一組資料萃取 API(至少 5 個方法)成具名 Tuple 回傳並寫一頁範例文件(8 小時)。

Assessment Criteria(評估標準)
- 功能完整性(40%):所有原邏輯與邊界條件皆正確移植。
- 程式碼品質(30%):命名清晰、無多餘暫存變數。
- 效能優化(20%):不引入額外裝箱與配置。
- 創新性(10%):具名元素運用合理、介面一致性佳。


## Case #2: 移除 out/ref 多回傳的侵入性:以 Tuples 精簡呼叫端

### Problem Statement(問題陳述)
- 業務場景:既有 API 使用 out/ref 傳回多個值,呼叫端必須先宣告暫存變數、程式碼冗長,且在非同步情境易造成混淆。
- 技術挑戰:維持型別安全與相容性的同時,降低呼叫端心智負擔。
- 影響範圍:呼叫端 LoC 偏高、可讀性差、測試/Mock 困難。
- 複雜度評級:中

### Root Cause Analysis(根因分析)
- 直接原因:
  1. out/ref 破壞單一回傳語意,增加暫存與前置宣告。
  2. 在 async/await 流程中 out/ref 不直觀。
  3. 語意模糊:讀者難以從簽章理解傳回值之間的關聯。
- 深層原因:
  - 架構層面:回傳結果未聚合表達。
  - 技術層面:缺乏語法層的「多值回傳」支援(C# 7 前)。
  - 流程層面:團隊慣用 out/ref 作為歷史包袱。

### Solution Design(解決方案設計)
- 解決策略:重構 out/ref API 為具名 Tuple 回傳,呼叫端以解構或具名屬性存取,降低侵入性。

- 實施步驟:
  1. 將簽章改為具名 Tuple
     - 實作細節:void GetSize(out int w, out int h) -> (int Width, int Height) GetSize()
     - 所需資源:C# 7 編譯器
     - 預估時間:0.5 小時
  2. 呼叫端改寫
     - 實作細節:var (w, h) = GetSize(); 或 var s = GetSize();
     - 所需資源:IDE/測試
     - 預估時間:0.5 小時
  3. 加註相容性注意事項
     - 實作細節:保留舊版 overload(過渡期)
     - 所需資源:版本控管
     - 預估時間:1 小時

- 關鍵程式碼/設定:
```csharp
// before
void GetSize(out int width, out int height)
{
  width = 800;
  height = 600;
}

// after
(int Width, int Height) GetSize() => (800, 600);

var (w, h) = GetSize();
  • 實際案例:將文中 GetSize(out int w, out int h) 改為具名 Tuple。
  • 實作環境:VS 15 Preview(Tuples 當時尚未完整支援;可先以概念與文件準備)
  • 實測數據:
    • 改善前:呼叫端需 4-6 行(宣告 + 呼叫 + 使用)
    • 改善後:2-3 行(解構/直接用屬性)
    • 改善幅度:LoC 減少約 40-60%,可讀性提升

Learning Points(學習要點)

  • 核心知識點:out/ref 的替代策略;具名 Tuple 的解構與存取。
  • 技能要求:
    • 必備技能:方法重構、單元測試。
    • 進階技能:API 延伸性與相容性設計。
  • 延伸思考:在公共 API 中是否保留相容 overload?如何以 Obsolete 屬性管理過渡?

Practice Exercise(練習題)

  • 基礎:重構 1 個 out/ref API(30 分鐘)
  • 進階:撰寫解構式呼叫 + 單元測試 + 例外處理(2 小時)
  • 專案:重構一組 out/ref API(≥5 個)並出遷移指南(8 小時)

Assessment Criteria

  • 功能完整性(40%):結果一致
  • 程式碼品質(30%):呼叫端簡潔
  • 效能優化(20%):無不必要配置
  • 創新性(10%):相容性策略妥善

Case #3: 擺脫 Item1/Item2 可讀性問題:以具名 Tuple 取代泛型 Tuple

Problem Statement(問題陳述)

  • 業務場景:已有程式碼使用 Tuple<int, int> 並以 Item1/Item2 存取,導致語意不清。
  • 技術挑戰:在不新增自訂類型的情況下,讓欄位具有語意化名稱。
  • 影響範圍:閱讀成本高、維護風險大。
  • 複雜度評級:低

Root Cause Analysis(根因分析)

  • 直接原因:
    1. Tuple<T1,T2> 只有 Item1/Item2 屬性名稱。
    2. 欄位語意只能靠註解補足。
  • 深層原因:
    • 架構層面:資料模型未語意化。
    • 技術層面:缺少語法層命名支援(C# 7 前)。
    • 流程層面:短期偷懶導致長期維護成本。

Solution Design(解決方案設計)

  • 解決策略:將 Tuple<T1,T2> 換為 C# 7 具名 Tuple,並以語意名稱存取。

  • 實施步驟:
    1. 以具名 Tuple 取代泛型 Tuple
      • 實作細節:Tuple<int,int> -> (int Width, int Height)
      • 預估時間:0.5 小時
    2. 全文替換 Item1/Item2
      • 實作細節:用 s.Width/s.Height 取代
      • 預估時間:0.5 小時
    3. 補充命名規範
      • 實作細節:文件化命名準則
      • 預估時間:0.5 小時
  • 關鍵程式碼/設定: ```csharp // before Tuple<int, int> GetSize() => new Tuple<int,int>(800, 600); var s = GetSize(); Console.WriteLine($”({s.Item1}, {s.Item2})”);

// after (int Width, int Height) GetSize() => (800, 600); var s = GetSize(); Console.WriteLine($”({s.Width}, {s.Height})”);


- 實際案例:文中 Size 範例。
- 實作環境:VS 15 Preview(Tuple 支援視當時狀態)
- 實測數據:可讀性提升;LoC 相近;效能無差異。

Learning Points
- 核心知識點:具名 Tuple 的價值在語意表達。
- 技能要求:重構與命名規範。
- 延伸思考:何時升級為 Record/POCO?

Practice Exercise
- 基礎:將 1 個 Tuple<T1,T2> 改為具名 Tuple(30 分鐘)
- 進階:在 3 個方法中推廣具名元素與解構使用(2 小時)
- 專案:制定團隊具名 Tuple 命名規範(8 小時)

Assessment Criteria
- 功能完整性(40%)
- 程式碼品質(30%)
- 效能優化(20%)
- 創新性(10%)


## Case #4: 臨時資料分組需求:使用 Tuple Literals 快速建構資料

### Problem Statement(問題陳述)
- 業務場景:僅需暫時性地將數值組合以便傳遞/印出/排序,不值得為其建立類別。
- 技術挑戰:在保持強型別與可讀性下,降低樣板程式碼。
- 影響範圍:避免 namespace 汙染與過度抽象。
- 複雜度評級:低

### Root Cause Analysis
- 直接原因:一時性資料結構無合適類型表達。
- 深層原因:
  - 架構:混合一時性資料與穩定模型。
  - 技術:過去缺少語法化臨時分組手段。
  - 流程:傾向用匿名型別但受限方法回傳。

### Solution Design
- 解決策略:以 Tuple Literals 直接產生具名 Tuple,滿足臨時需求。

- 實施步驟:
  1. 使用 Tuple Literals 宣告
     - 實作細節:var size = (width: 800, height: 600);
     - 預估時間:即時
  2. 傳遞或回傳 Tuple
     - 實作細節:作為方法回傳或集合元素
     - 預估時間:即時

- 關鍵程式碼/設定:
```csharp
// tuple literals demo
var size = (width: 800, height: 600);
Console.WriteLine(size.width * size.height);
  • 實際案例:文中 tuple literals 範例。
  • 實作環境:VS 15 Preview(Tuple 支援視當時狀態)
  • 實測數據:LoC 顯著下降;不新增型別;效能等同。

Learning Points

  • 核心:Tuple 與匿名型別的取捨。
  • 技能:臨時資料建模。
  • 延伸:何時需要升級為 Record。

Practice Exercise

  • 基礎:用 Tuple 組合三個欄位並印出(30 分鐘)
  • 進階:以 Tuple 作為 LINQ 結果、排序與分頁(2 小時)
  • 專案:以 Tuple 架構小型資料管線(8 小時)

Assessment Criteria

  • 功能(40%)
  • 品質(30%)
  • 效能(20%)
  • 創新(10%)

Case #5: 避免僅用一次的類別污染命名空間:以 Tuples 取代一次性 POCO

Problem Statement

  • 業務場景:某些中介結果只會用一次,但過去需建立 POCO 類別對應,造成型別爆炸。
  • 技術挑戰:在維持清晰語意的情況下降低型別數量。
  • 影響範圍:命名空間雜訊、維護成本增加。
  • 複雜度評級:低

Root Cause Analysis

  • 直接原因:為了強型別,不得不宣告類別。
  • 深層原因:
    • 架構:缺少臨時語意載體。
    • 技術:語法支援不足。
    • 流程:未建立資料形狀分級策略。

Solution Design

  • 解決策略:一次性資料以具名 Tuple 表達,避免宣告多餘型別。

  • 實施步驟:
    1. 識別一次性資料形狀
    2. 改以具名 Tuple 表達
    3. 文件化「一次性用 Tuple、重用型升級為 Record/POCO」
  • 關鍵程式碼/設定:
    // before: class Size { int Width; int Height; }
    // after:
    (int Width, int Height) Resize(...) => (800, 600);
    
  • 實際案例:Size 範例中若僅使用一次。
  • 實作環境:同上
  • 實測數據:型別數量下降;LoC 減少;維護成本下降。

Learning Points

  • 資料形狀分級策略:Tuple(一次性)、Record(穩定值物件)、POCO(富模型)。
  • 技能:演進式建模。

Practice Exercise

  • 基礎:把 1 個一次性 POCO 改成具名 Tuple(30 分鐘)
  • 進階:設計規範並套用至 3 個案例(2 小時)
  • 專案:審視一個模組的資料形狀並重新分級(8 小時)

Assessment Criteria

  • 功能(40%)
  • 品質(30%)
  • 效能(20%)
  • 創新(10%)

Case #6: 大量 POCO 樣板痛點:以 Record Type 一行宣告值物件

Problem Statement

  • 業務場景:從資料庫/JSON 撈出資料,需要大量只含屬性的類別;傳統宣告冗長。
  • 技術挑戰:降低樣板、保留強型別、支援值相等比較與不可變。
  • 影響範圍:開發效率、程式碼可讀性、錯誤率。
  • 複雜度評級:中

Root Cause Analysis

  • 直接原因:POCO 宣告需手寫屬性、建構子、Equals/GetHashCode。
  • 深層原因:
    • 架構:資料模型以值物件為主。
    • 技術:需要值相等、不可變特性,手寫成本高。
    • 流程:靠 CodeGen,修改困難。

Solution Design

  • 解決策略:以 Record Type 宣告 Read-only Properties 的值物件,一行到位,自動 IEquatable

  • 實施步驟:
    1. 以 Record 定義值物件
      • 實作細節:class Person(string First, string Last);
      • 預估時間:即時
    2. 以 with 進行更新
      • 實作細節:var me2 = me with { First = “Peter” };
    3. 規範:資料物件以 Record 優先;含行為再升級為類別
  • 關鍵程式碼/設定: ```csharp class Person(string First, string Last);

var me = new Person { First = “Andrew”, Last = “Wu” }; var myson = me with { First = “Peter” }; // 產生新物件(不可變)


- 實際案例:文中 Person/幾何形狀宣告。
- 實作環境:VS 15 Preview(當時 Records 尚未支援;先以設計指引為主)
- 實測數據:宣告從數十行縮至 1 行;值相等免手寫;效能不變(語法糖)。

Learning Points
- 核心:Record 的不可變/值相等特性。
- 技能:值物件建模、演進策略。
- 延伸:與 Tuples 的選擇與升級路徑。

Practice Exercise
- 基礎:把 1 個 POCO 換成 Record(30 分鐘)
- 進階:用 with 改 3 個欄位並驗證舊物件不變(2 小時)
- 專案:建立 10 個資料表對應的 Records(8 小時)

Assessment Criteria
- 功能(40%)
- 品質(30%)
- 效能(20%)
- 創新(10%)


## Case #7: 安全更新值物件:以 Record 的 with 構造維持不可變性

### Problem Statement
- 業務場景:對資料做「更新」但需保留不可變特性(如審計/快照)。
- 技術挑戰:避免原地修改,降低共享狀態風險。
- 影響範圍:一致性、並行安全、可測性。
- 複雜度評級:低

### Root Cause Analysis
- 直接原因:傳統類別屬性可變,易被誤改。
- 深層原因:
  - 架構:需要不可變以利併發與推理。
  - 技術:手工複製成本高。
  - 流程:缺少標準更新模式。

### Solution Design
- 解決策略:採用 Record + with,透過新物件替換表達變更。

- 實施步驟:
  1. 導入 Record 值物件
  2. 用 with 表達變更
  3. 在業務邏輯層確立「不可變更新」規範

- 關鍵程式碼/設定:
```csharp
class Person(string First, string Last);
var me = new Person { First = "Andrew", Last = "Wu" };
var updated = me with { First = "Peter" }; // me 不變
  • 實際案例:Person 範例。
  • 實作環境:如上
  • 實測數據:共享狀態 Bug 機率下降;程式碼更直觀。

Learning Points

  • 不可變的價值、with 模式。
  • 技能:狀態管理、快照策略。
  • 延伸:各層不可變策略一致性。

Practice Exercise

  • 基礎:以 with 更新 1 個欄位(30 分鐘)
  • 進階:實作不可變的審計記錄(2 小時)
  • 專案:將 3 個業務物件改成不可變與 with 更新(8 小時)

Assessment Criteria 同上

Case #8: 值相等比較的正確實作:Record 自動 IEquatable

Problem Statement

  • 業務場景:需要以值比較(Value Equality)判定兩筆資料是否相同(非參考相等)。
  • 技術挑戰:手寫 Equals/GetHashCode 易錯且冗長。
  • 影響範圍:集合操作、比較、快取鍵值。
  • 複雜度評級:低

Root Cause Analysis

  • 直接原因:傳統類別為參考相等,值比較需手寫。
  • 深層原因:
    • 架構:值物件為主。
    • 技術:樣板多、錯誤率高。

Solution Design

  • 解決策略:Record 型別自動實作 IEquatable,用於值比較。

  • 實施步驟:
    1. 將需要值比較的 POCO 改成 Record
    2. 撰寫單元測試驗證值相等行為
  • 關鍵程式碼/設定: ```csharp class Person(string First, string Last);

var a = new Person { First=”A”, Last=”B” }; var b = new Person { First=”A”, Last=”B” }; Console.WriteLine(a.Equals(b)); // True(值相等)


- 實際案例:Record 自動值相等說明。
- 實作環境:如上
- 實測數據:樣板程式碼歸零;比較正確性提高。

Learning Points:值物件 vs 參考物件。
Practice:撰寫值相等測試。
Assessment:同前。


## Case #9: 快速建模繼承樹:以 Records + 繼承定義幾何型別

### Problem Statement
- 業務場景:需要快速定義 Triangle/Rectangle/Square 等多邊形資料。
- 技術挑戰:傳統類別宣告冗長、迭代慢。
- 影響範圍:建模效率、樣板多。
- 複雜度評級:中

### Root Cause Analysis
- 直接原因:多型別資料宣告成本高。
- 深層原因:
  - 架構:需要清晰的型別階層。
  - 技術:樣板程式碼造成摩擦。

### Solution Design
- 解決策略:用 Record 一行宣告並支援繼承,快速建立型別樹。

- 實施步驟:
  1. 宣告基底與子型別
  2. 以建構子參數定義屬性
  3. 搭配 Pattern Matching 使用

- 關鍵程式碼/設定:
```csharp
public class Geometry();
public class Triangle(int Side1, int Side2, int Side3) : Geometry;
public class Rectangle(int Width, int Height) : Geometry;
public class Square(int width) : Geometry;
  • 實際案例:文中幾何型別宣告。
  • 實作環境:如上
  • 實測數據:宣告 LoC 減少 >80%。

Learning Points:資料型別階層的輕量建模。 Practice:為 3 種業務實體建繼承樹。 Assessment:同前。

Case #10: 異質集合計算繁瑣:以 Pattern Matching 精簡計算流程

Problem Statement

  • 業務場景:對一組不同型別的幾何物件計算總面積。
  • 技術挑戰:過去需 is + cast + if/else/switch,大量暫存與重覆程式碼。
  • 影響範圍:可讀性差、易錯。
  • 複雜度評級:中

Root Cause Analysis

  • 直接原因:型別判斷與轉型分離。
  • 深層原因:
    • 架構:異質集合常見。
    • 技術:缺少語法支援的「型別+條件」配對。

Solution Design

  • 解決策略:使用 C# 7 Pattern Matching(switch/if)將型別測試與變數綁定合併,直接在 case 區塊計算。

  • 實施步驟:
    1. 使用 switch (s) case Triangle x: 等語法
    2. 將公式內嵌至對應 case
    3. 加入 when 篩選(若有額外條件)
  • 關鍵程式碼/設定:
    int total_area = 0;
    foreach(Geometry s in shapes)
    {
    switch (s)
    {
      case Triangle x:
        int t = (x.Side1 + x.Side2 + x.Side3) / 2;
        total_area += (int)Math.Sqrt(t * (t - x.Side1) * (t - x.Side2) * (t - x.Side3));
        break;
      case Rectangle x:
        total_area += x.Width * x.Height;
        break;
      case Square x:
        total_area += x.Width * x.Width;
        break;
    }
    }
    
  • 實際案例:文中 Demo1。
  • 實作環境:VS 15 Preview(Pattern Matching 可編譯)
  • 實測數據:
    • 改善前:is + cast + if/else,多次宣告與轉型
    • 改善後:單一語句同時判斷+綁定,LoC 減少 20-40%
    • 效能:IL 近似 if/else,無顯著差異

Learning Points:Pattern Matching 核心語法。 Practice:為 3 種型別撰寫對應處理。 Assessment:同前。

Case #11: 移除 is+cast 雜訊:以 Pattern Variable 合併型別測試與綁定

Problem Statement

  • 業務場景:頻繁出現「先 is,再 cast」的程式碼樣式,冗長且易誤用。
  • 技術挑戰:減少重覆與潛在 InvalidCastException。
  • 影響範圍:可讀性、可靠性。
  • 複雜度評級:低

Root Cause Analysis

  • 直接原因:需要兩步驟才完成一個邏輯。
  • 深層原因:語法缺少「同時判斷與命名」的模式。

Solution Design

  • 解決策略:使用 if (obj is Type x) 直接完成測試與綁定。

  • 關鍵程式碼/設定:
    if (s is Rectangle x)
    {
    total_area += x.Width * x.Height;
    }
    
  • 實際案例:文中 if/else 與 switch 對照。
  • 實測數據:LoC 減少、例外風險下降。

Learning Points:Pattern variable 用法。 Practice:將 3 段 is+cast 改寫。 Assessment:同前。

Case #12: 以 when 子句完成型別+屬性條件的 Pattern 篩選

Problem Statement

  • 業務場景:僅統計高度>5 的幾何物件面積。
  • 技術挑戰:型別與屬性條件需同時成立。
  • 影響範圍:複雜條件邏輯分散。
  • 複雜度評級:中

Root Cause Analysis

  • 直接原因:過去需巢狀 if,邏輯分裂。
  • 深層原因:缺少「型別模式 + 條件」的語法整合。

Solution Design

  • 解決策略:使用 switch case X when 條件。

  • 關鍵程式碼/設定:
    switch (s)
    {
    case Triangle x when x.Side1 > 5:
      // ...
      break;
    case Rectangle x when x.Height > 5:
      // ...
      break;
    case Square x when x.Width > 5:
      // ...
      break;
    }
    
  • 實際案例:文中 when 範例。
  • 實測數據:條件集中、可讀性提升。

Learning Points:when 子句。 Practice:加入 2 個額外條件。 Assessment:同前。

Case #13: 多型 vs Pattern Matching:選型準則與重構

Problem Statement

  • 業務場景:同樣是計算面積,加總邏輯可分散在多處或集中一處,該用多型還是 Pattern Matching?
  • 技術挑戰:在封裝性與直覺性間取捨。
  • 影響範圍:重用性、耦合度、測試策略。
  • 複雜度評級:高

Root Cause Analysis

  • 直接原因:兩種風格皆可達成需求。
  • 深層原因:
    • 架構:封裝與開放封閉原則。
    • 技術:Pattern Matching 易把邏輯集中於單處。
    • 流程:需求是否多處重用。

Solution Design

  • 解決策略:
    • 若運算會在多處重用、且屬於物件本質行為,採多型(GetArea())。
    • 若資料由外部來源(DB/JSON)無法封裝行為或需求偶發,採 Pattern Matching。
  • 實施步驟:
    1. 盤點行為重用點
    2. 選擇策略與重構
    3. 撰寫測試以覆蓋兩種風格
  • 關鍵程式碼/設定(多型片段):
    int total = 0;
    foreach (var s in shapes) total += s.GetArea();
    
  • 實際案例:文中兩個版本對比。
  • 實測數據:可讀性依場景而定;效能無顯著差異。

Learning Points:設計權衡。 Practice:為 1 個模組做決策紀錄 ADR。 Assessment:同前。

Case #14: 驗證語法糖本質:以 ILDASM/Reflector 觀察編譯產物

Problem Statement

  • 業務場景:團隊擔心新語法會影響效能或相容性。
  • 技術挑戰:需要證據證明語法糖等價展開,不改變 IL 本質。
  • 影響範圍:導入決策、風險接受度。
  • 複雜度評級:中

Root Cause Analysis

  • 直接原因:不了解編譯器如何展開新語法。
  • 深層原因:缺少 IL 層面的驗證流程。

Solution Design

  • 解決策略:用 ILDASM 與 .NET Reflector 反組譯觀察 switch + pattern matching 的 IL 與 C#6 展開。

  • 實施步驟:
    1. 編譯含 Pattern Matching 的程式
    2. 用 ILDASM 檢視 IL
    3. 用 Reflector 還原為 C#6 對照
  • 關鍵程式碼/設定:
    // 工具:ILDASM、.NET Reflector
    // 結論:switch pattern -> if/else + as + null 檢查,效能等價
    
  • 實際案例:文中反組譯截圖與還原程式片段。
  • 實作環境:VS 15 Preview + 工具安裝
  • 實測數據:IL 指令序列等價,無額外開銷。

Learning Points:語法糖與 IL 關係。 Practice:對 1 段程式做 IL 對照報告。 Assessment:同前。

Case #15: 啟用 C# 7 預覽語法:定義條件編譯符號

Problem Statement

  • 業務場景:VS 15 Preview 安裝後仍無法使用新語法。
  • 技術挑戰:新語法預設停用,需要專案層設定。
  • 影響範圍:無法編譯或 IDE 報錯。
  • 複雜度評級:低

Root Cause Analysis

  • 直接原因:語言功能在 Preview 階段需以符號顯式啟用。
  • 深層原因:風險控管、相容性。

Solution Design

  • 解決策略:在專案屬性 Build 中新增條件編譯符號 DEMO, DEMO_EXPERIMENTAL

  • 實施步驟:
    1. 專案 > 屬性 > Build
    2. Conditional compilation symbols 加入:DEMO;DEMO_EXPERIMENTAL
    3. 重建專案驗證
  • 關鍵程式碼/設定:
    // Project > Properties > Build > Conditional compilation symbols:
    // __DEMO__;__DEMO_EXPERIMENTAL__
    
  • 實際案例:文中截圖教學。
  • 實作環境:VS 15 Preview
  • 實測數據:編譯與 IntelliSense 錯誤消失,能體驗支援的語法(Tuples/Records 當時尚未支援)。

Learning Points:預覽功能開關。 Practice:建立一個可編譯的 Pattern Matching 範例。 Assessment:同前。

Case #16: 舊平台相容性顧慮:語法糖不需新 Runtime

Problem Statement

  • 業務場景:擔心新語法產生的組件無法在舊平台執行。
  • 技術挑戰:釐清語法糖與 Runtime 的關係。
  • 影響範圍:部署策略、升級時程。
  • 複雜度評級:低

Root Cause Analysis

  • 直接原因:誤認為新語法需新 Runtime。
  • 深層原因:語言與執行時的責任邊界不清。

Solution Design

  • 解決策略:教育宣導 C# 7 所示功能皆屬編譯器層級(語法糖),不需新 Runtime;可在舊平台執行。

  • 實施步驟:
    1. 文件化說明
    2. 實測在舊目標 Framework 上執行
    3. 建立常見問答
  • 關鍵程式碼/設定:無(觀念性)
  • 實際案例:文中明確說明無需新 Runtime。
  • 實測數據:在既有環境執行成功。

Learning Points:語言/編譯器/Runtime 分工。 Practice:在現有 CI 上建置運行示例。 Assessment:同前。

Case #17: 預覽期不支援 Tuples/Records 的替代方案與測試策略

Problem Statement

  • 業務場景:VS 15 Preview 期間,Tuples/Records 尚未支援,導致示例無法編譯。
  • 技術挑戰:仍需驗證設計與教學。
  • 影響範圍:學習推進、PoC。
  • 複雜度評級:中

Root Cause Analysis

  • 直接原因:IDE/編譯器當前不支援。
  • 深層原因:預覽版分階段落地。

Solution Design

  • 解決策略:採用「可替代方案 + 可支援範圍內的語法」雙軌測試:Tuples 用 Tuple/out 代替;Records 用傳統 POCO;Pattern Matching 可直接測。

  • 實施步驟:
    1. Tuples:先用 Tuple<T1,T2> 或 out 參數
    2. Records:用 POCO + 手寫 Equals(或暫略)
    3. Pattern Matching:直接在 VS 15 Preview 測試
    4. 待支援落地後切換回新語法
  • 關鍵程式碼/設定:
    // Tuples 暫代
    Tuple<int,int> GetSize() => Tuple.Create(800, 600);
    // 或
    void GetSize(out int w, out int h) { w=800; h=600; }
    
  • 實際案例:文中提及 Tuples/Records 當時尚不可跑。
  • 實作環境:VS 15 Preview
  • 實測數據:教學與 PoC 不被阻斷;後續切換成本可控。

Learning Points:預覽期迭代策略。 Practice:以雙軌方案寫 1 組範例並記錄切換步驟。 Assessment:同前。


案例分類

  1. 按難度分類
    • 入門級(適合初學者)
      • Case 3, 4, 5, 7, 8, 11, 12, 15, 16
    • 中級(需要一定基礎)
      • Case 1, 2, 6, 9, 10, 17
    • 高級(需要深厚經驗)
      • Case 13, 14
  2. 按技術領域分類
    • 架構設計類
      • Case 5, 6, 7, 8, 9, 13, 16, 17
    • 效能優化類
      • Case 14(效能驗證/不引入負擔)、10(可讀性間接影響維護效能)
    • 整合開發類
      • Case 1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 17
    • 除錯診斷類
      • Case 11, 14
    • 安全防護類
      • Case 7(不可變、並行安全取向)、16(部署相容性風險控管)
  3. 按學習目標分類
    • 概念理解型
      • Case 14, 16
    • 技能練習型
      • Case 3, 4, 7, 8, 11, 12, 15
    • 問題解決型
      • Case 1, 2, 5, 6, 9, 10, 17
    • 創新應用型
      • Case 13(設計取捨與重構策略)

案例關聯圖(學習路徑建議)

  • 建議先學: 1) Case 15(啟用預覽語法)→ 2) Case 16(了解語法糖與相容性)→ 3) Case 11(pattern 變數基礎)→ 4) Case 12(when 子句)
  • 進一步:
    • Tuples 線:Case 3(具名 Tuple)→ Case 4(Tuple literals)→ Case 1(多回傳 API)→ Case 2(替代 out/ref)→ Case 5(避免一次性 POCO)
    • Records 線:Case 6(Record 基礎)→ Case 7(with 不可變)→ Case 8(值相等)→ Case 9(繼承樹)
    • Pattern Matching 線:Case 10(異質集合計算)→ 與 Records/繼承結合練習
  • 交叉與決策:
    • Case 13(多型 vs Pattern Matching)依賴 Tuples/Records/Pattern 基礎,建議在上述三線完成後學。
    • Case 14(ILDASM/Reflector 驗證)放在最後,作為原理驗證與效能/相容性佐證。
    • Case 17(預覽替代方案)可在任何時點輔助教學與 PoC。

完整學習路徑建議:

  • 啟用與原理:Case 15 → 16 → 11 → 12
  • 功能三線並進(可交錯):
    • Tuples:3 → 4 → 1 → 2 → 5
    • Records:6 → 7 → 8 → 9
    • Pattern:10(可與 Records/繼承合練)
  • 綜合決策與原理驗證:13 → 14
  • 預覽期應對:17

此路徑可在 2-4 週內完成由語法、模式、設計決策到原理驗證的完整學習閉環。






Facebook Pages

AI Synthesis Contents

- 原始文章內容
- 問答集
- 文章摘要
- 解決方案 / Case Study

Edit Post (Pull Request)

Post Directory