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
開發環境 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<IPerson> Create()
{
return 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= 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<string,int>。
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<string,int>。
寫起來超直覺,我覺得這功能真是超嗨的。