C# 9.0 功能預覽 (4) patterns matching

再來也是個挺刺激的話題,pattern matching 在 C# 9.0  的增強。

環境
Visual Studio 2019 - 16.8.0 Preview 3.2
.NET 5.0.0-rc.1.20451.14

C# 9.0 帶來了幾個新的 pattern matching 擴充,分別為以下:

(1) Relational patterns
(2) Logical patterns
(3) Not patterns
(4) Simple type patterns

Relational patterns

Relational patterns 就是關係運算子 >、<、>= 和 >= 的運用,舉個簡單的需求,假設把矩形丟進一個函式,這個函式依賴 switch 分支依據其面積大小輸出特定字串。

先建立必要的介面與類別


 class Rectangle 
 {
     public double Width { get; set; }
     public double Height { get; set; }
     public double GetArea()
     {
         return Width * Height;
     }
 }
 

  過去在 C# 8 我們得使用 when 子句:

 static string Display8(Rectangle  rect) => rect.GetArea()  switch
 {       
     double area when area < 10 => "小矩形",
     double area when area >= 100 => "大矩形",            
     _ => "中矩形"
 };

C# 9 的 relational patterns 則簡化的 when 子句的使用:

 static string Display9(Rectangle rect) => rect.GetArea() switch
 {
     < 10 => "小矩形",
     >= 100 => "大矩形",
     _ => "中矩形"
 };

再舉另外一個可以使用 relationl patterns 的例子,這個例子應用了好幾個 patterns 混合,我們要從一群學生中挑出分數超過七十分且為男性的學生:

  interface IPerson
    {
        Guid ID { get; set; }
        string Name { get; set; }
    }

  class Teacher : IPerson
    {
        public Guid ID { get; set; }
        public string Name { get; set; }
        public string Subject { get; set; }
    }

  class Student : IPerson
    {
        public Guid ID { get; set; }
        public string Name { get; set; }

        public bool IsPassed
        {
            get { return Score > 59; }
        }

        public Gender Gender { get; set; }

        public int Score { get; set; }
    }

  public enum Gender
    {
        Male, Female
    }

測試資料的內容如下:

 new List<IPerson>
 {
     new Teacher {Name= "Bill" },
     new Teacher {Name= "David"},
     new Student{ Name = "魯夫",  Gender = Gender.Male , Score= 60},
     new Student{ Name = "妮可羅賓",  Gender = Gender.Female , Score= 82},
     new Student{ Name = "娜美",   Gender = Gender.Female, Score= 70 },
     new Student{ Name = "騙人布" ,Gender = Gender.Male, Score= 55 },
     new Student{ Name = "香吉士",  Gender = Gender.Male, Score= 58 },
     new Student{ Name = "喬巴",   Gender = Gender.Male, Score= 67 },
     new Student{ Name = "布魯克",  Gender = Gender.Male, Score= 99 },
     new Student{ Name = "索隆",  Gender = Gender.Male, Score= 80 },
     new Student{ Gender = Gender.Male , Score= 60},                      
 };

過去在 C# 8 你得這樣寫:

 static IEnumerable<string> GetStudentOver70_8()
 {
     var people = Create();
     foreach (var p in people)
     {

         if (p is Student { Gender: Gender.Male, Name: string name, Score: int score } && score > 70)
         {
             yield return name;
         }
     }
 }

C# 9 有了 relational patterns 後你可以這樣寫:

 static IEnumerable<string> GetStudentOver70_9()
 {
     var people = Create();
     foreach (var p in people)
     {

         if (p is Student { Gender: Gender.Male, Name: string name, Score: > 70 })
         {
             yield return name;
         }
     }
 }

簡單多了,對吧。

Logical patterns

 Logical patterns 說來就是可以在 patterns matching 的語法內組合 and、or 和 not 運算子,很有趣的是它並沒有採用過去習慣的 &&、|| 和 ! 來表示。

拿上一個學生分數的例子,做一個無聊的例題,找出 >=90 或 < 10 分的給予 "特別高或特別低";剩下的則給"一般",程式碼如下:

 class Program
 {
     static void Main(string[] args)
     {
         foreach (var student in Create())
         {
             Console.WriteLine($"{student.Name} is {GetGrades(student)}");
         }
     }

     static string GetGrades(Student student) => student.Score switch
     {
        >=90 or <10 => "特別高或特別低",
        _ => "一般"
     };
     

     static List<Student> Create()
     {
         return new List<Student>
                {                      
                    new Student{ Name = "魯夫",  Gender = Gender.Male , Score= 5},
                    new Student{ Name = "妮可羅賓",  Gender = Gender.Female , Score= 82},
                    new Student{ Name = "娜美",   Gender = Gender.Female, Score= 70 },
                    new Student{ Name = "騙人布" ,Gender = Gender.Male, Score= 55 },
                    new Student{ Name = "香吉士",  Gender = Gender.Male, Score= 58 },
                    new Student{ Name = "喬巴",   Gender = Gender.Male, Score= 67 },
                    new Student{ Name = "布魯克",  Gender = Gender.Male, Score= 99 },
                    new Student{ Name = "索隆",  Gender = Gender.Male, Score= 80 },                    
                };
     }
 }

另外兩個我們下次再聊。

參考資料:模式比對增強功能