C# 8.0 抢先看 — Recursive patterns

Recursive patterns 是一个非常强大的语法糖,个人觉得真的把‘懒还要更懒’发挥到极致。
玩转 WPF 课程招生中,传送门:https://skilltree.my/events/9cbgp


本篇文章使用环境
开发环境 Visual Studio 2019 Preview 4 (16.0.0 Preview 4)
框架       .NET Core 3.0.100-preview-010184
编译器    C# 8.0 beta

C# 7.0 新增了 pattern matching 的功能,降低程序中需要转型与比对的困扰,本来我以为这已经很猛了,但更猛的还在后头。C# 8.0 为 pattern matching 衍生了许多的变化形,其中一项就是 recursive patterns,这玩意用在 Composite object 时特别会让人有一种愉悦感。

以下拿个简单的范例展示一下 recurisve 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
    }


有两个类分别实践了 IPerson -- Teacher class 与 Student class。接着写个制作测试用数据的方法:

  static List Create()
  {
      return new List
      {
          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= 42 },
          new Student{ Name = "索隆",  Gender = Gender.Male, Score= 80 },
          new Student{ Gender = Gender.Male , Score= 60},
          new Student{ Name = string.Empty,  Gender = Gender.Male , Score= 99},
      };
  }


准备工作都完成了,现在来假设一个情境,如果需求是从这个 List 中取得以下条件的数据 (1) 学生 (2) 男性 (3) Name 不可为 null (4) 有通过测验,亦即 IsPassed 为 true。在 C# 7.0 时可能会这样写 (这边我用上了 VauleTuple 作为回传的类型):

 static IEnumerable<(string Name, int Score)> GetStudentIsPassedAndMale()
 {
     var people = Create();

     return people.Where((x) => x is Student student 
                         && student.IsPassed == true 
                         && student.Gender == Gender.Male 
                         && student.Name != null).Select((y) =>
                                                  {
                                                      Student s = y as Student;
                                                      return (s.Name, s.Score);
                                                  });
 }

其实个人觉得这写法已经很简单了,至少在类型比对转换少了些工要做。但如果是使用  C# 8.0 的 recursive patterns,更是简单到令人难以置信 。以下为使用 recursive patterns 的程序:

 static IEnumerable <(string Name, int Score)> GetStudentIsPassedAndMale()
 {    
     var people = Create();
     foreach (var p in people)
     {
         if (p is Student { Gender: Gender.Male, IsPassed: true, Name: string name, Score: int score })
         {
             yield return (name, score);
         }
     }         
 }

说明如下:

1. p is Student 代表只有选择 Student 类的执行个体。

2. Gender: Gender.Male, IsPassed: true 表示选择的条件为 Gender 属性为 Gender.Male 且 IsPassed 属性为 true 者。

3. Name: string name 表示如果 Name 属性不为 null 者 (如果是 null 会被排除于结果外),将其 Name 属性的值设定给 string 类型的 name 变量。

4. Score: int score 表示将其 Score 属性值设定给 int 类型的 score 变量。

5. 最后 yield return ValueTuple

 写起来超直觉,我觉得这功能真是超嗨的。

范例位于:Recursive patterns samples