在C#的Switch Expression下使用模式比對(Pattern Matching)

C# 9.0再度增強了模式比對(Pattern matching enhancements)的表述能力後,再佐以各式的Recursive Pattern Matching,讓整個Switch運算式(Switch Expressions)撰寫起來已經接近外星語言等級了,實在很擔心C#再繼續往這種極簡的函式語言(Functional Programming;FP)靠攏,若整個官網若都充斥著此類語法範例,對於初學者而言這個語言大概會被放棄掉吧...

這邊透過Switch運算式:SwitchExpression的結構(傳統寫法叫做Switch語句:SwitchStatement),把模式比對的寫法綜合表述一下,供給有需要的人員參考。

SwitchExpression於C# 9.0增強範例

不囉嗦,直接上程式碼

public class Program
{
  public static void Main()
  {
    Show(Check( 0, "TheBOYSayWANNADrink"));
    Show(Check( 1, "TheGirlSayWannaEat"));
    Show(Check( 2, "OthersString"));
    Show(Check( 3, -100));
    Show(Check( 4, 123));
    Show(Check( 5, 199));
    Show(Check( 6, 201));
    Show(Check( 7, ("John", 17)));
    Show(Check( 8, ("John", 18)));
    Show(Check( 9, ("John", 19)));
    Show(Check(10, new Human1("John", 17)));
    Show(Check(11, new Human1("John", 18)));
    Show(Check(12, new Human1("John", 19)));
    Show(Check(13, new Human2() { cName="John", iAge = 17}));
    Show(Check(14, new Human2() { cName="John", iAge = 18}));
    Show(Check(15, new Human2() { cName="John", iAge = 19}));
    Show(Check(16, true));
  }
	
  public static string Check(int iIndex, System.Object oTemp) => oTemp switch
  { //string
    string x when x.Equals("TheBoySayWannaDrink", System.StringComparison.CurrentCultureIgnoreCase) => $"{iIndex}. 字串A:傳入值為 / {x}",
    "TheGirlSayWannaEat" => $"{iIndex}. 字串B",
    string => $"{iIndex}. 只知道是字串",
    
    //int
    int x and < 0 => $"{iIndex}. 整數A:傳入值為 / {x}",
    123 => $"{iIndex}. 整數B:",
    int and > 0 and ( >= 100 and <= 200) => $"{iIndex}. 整數C",
    int => $"{iIndex}. 只知道是整數",
    
    //VauleTuple
    System.ValueTuple<string, int> x when x == ("John", 17) => $"{iIndex}. ValueTupleA:傳入值為 / {x.Item1}",
    ("John", 18) => $"{iIndex}. ValueTupleB",
    (_, _) => $"{iIndex}. 只知道是ValueTuple",
    
    //ClassObject-1
    Human1 { cName: "John", iAge: 17 } x => $"{iIndex}. 物件A:傳入值為 / {x.cName}",
    Human1("John", 18) => $"{iIndex}. 物件A",
    Human1 x => $"{iIndex}. 只知道是 / {x.GetType()}",

    //ClassObject-2
    Human2 { cName: "John", iAge: 17 } x => $"{iIndex}. 物件B:傳入值為 / {x.cName}",
    Human2 { cName: "John", iAge: 18 } => $"{iIndex}. 物件B",
    Human2 x => $"{iIndex}. 只知道是 / {x.GetType()}",
	
    //default
    _ => $"{iIndex}. 無法識別物件"
  };
  
  public static void Show(string cTemp) =>  Console.WriteLine(cTemp);

  public record Human1(string cName, int iAge);

  public class Human2
  {
    public string cName;
    public int iAge;
  }
}

輸出結果如下:

 0. 字串A:傳入值為 / TheBOYSayWANNADrink
 1. 字串B
 2. 只知道是字串
 3. 整數A:傳入值為 / -100
 4. 整數B:
 5. 整數C
 6. 只知道是整數
 7. ValueTupleA:傳入值為 / John
 8. ValueTupleB
 9. 只知道是ValueTuple
10. 物件A:傳入值為 / John
11. 物件A
12. 只知道是 / Program+Human1
13. 物件B:傳入值為 / John
14. 物件B
15. 只知道是 / Program+Human2
16. 無法識別物件

補充說明:

  1. 程式碼中的整數那邊的判斷邏輯有點問題是我故意寫的(大於等於100肯定大於零),因為我只是想要表示可以用and or等語法。
  2. SwitchExpression在編譯時期會幫你檢測條件順序邏輯並給予警告,例如把數值判斷「123描述判斷」這行直接與下一行判斷交換,編譯器會直接告訴你錯誤(因為上面行號的邏輯已經包含了「123描述判斷」這行的邏輯),是一個非常有趣的行為。
  3. Record類別在PatternMatching時期可以用回類似「後宣告」而非「建構式宣告」的寫法,令人激賞。

相關連結:

  1. 利用C#的switch case when語法來忽略字串大小寫
  2. 遞迴模式比對
  3. C# 9.0 中的新增功能
SwitchExpression SwitchStatement RecursivePatternMatching C#9.0 C#8.0 FunctionalProgramming FP 函數式 函式語言程式設計