本文參考Roslyn項目Issue:#206,及Docs:#patterns。
1. C# 7.0 新特性1: 基於Tuple的“多”返回值方法
2. C# 7.0 新特性2: 本地方法
3. C# 7.0 新特性3: 模式匹配
模式匹配也許能算的上C#本次更新最重量級的升級,也是最受關注的特性(也許沒有之一),通過模式匹配,我們可以簡化大量的條件代碼。
Switch語句
大家也許遇到過這樣的情景,假設你的代碼中,有一個Nullable<int>的值,需要對其在正整數,非正整數,Null三種情況下分別作不同的邏輯處理。大多數童鞋直接想到是類似於下面的邏輯:
1 void Foo(int? num) 2 { 3 if (!num.HasValue) 4 /* null logic */ 5 else if (num.Value > 0) 6 /* positive int logic */ 7 else 8 /* negative int & zero logic */ 9 } View Code請大家思考一下,這個邏輯是否可以用switch-case語句來做,在VB及很多非C系的語言中,答案是肯定的,比如VB.NET中可以這樣寫:
1 Sub Foo(Num As Integer?) 2 Select Case Num 3 Case Not Num.HasValue 4 'null logic 5 Case Num > 0 6 'positive Int logic 7 Case Num <= 0 8 'negative Int() & zero logic 9 Case Else 10 11 End Select 12 End Sub View Code說到這裡,在具體討論模式匹配在switch-case中的應用之前,先淡淡的吐槽一下C#,本來理所應當的一個簡單的小語法,到了C#7.0才加入。
看看C#7.0加入的類型模式(Type Pattern):
1 void Foo(int? num) 2 { 3 switch (num) 4 { 5 case null: 6 //null logic 7 break; 8 case int n when n > 0: 9 //positive Int logic 10 break; 11 case int n when n <= 0: 12 //negative Int() & zero logic 13 break; 14 } 15 }
這個不多說了,大家自己體會,單純的在Nullable<int>下,可能體現的不是很清晰,個人認為這個小變動其實意義並不是很大,同樣場景下,或許if-if else-else會讓代碼更清晰易讀些。
如果說模式匹配僅僅是完善了一下switch-case,那可真是太大才小用了,下面我們看一個好玩的。
Match表達式
雖然把match帶到C#中看起來並不是什麼大事,但是會引起的代碼簡化還是非常爽的。
就像很多人說三元表達式(<condition>?<true result> : <false result> )將if-else簡化一樣。match表達式,是將switch-case結構簡化到了一個新限度。
看match表達式代碼前,我們先來看一行略坑的三元表達式。
var reuslt = x == null ? default(int) : (x is Func<int> ? (x as Func<int>)() : (x is int ? Convert.ToInt32(x) : default(int)));
好吧,我承認我是故意讓你們抓狂的。^_^, 為了能穩住大家看完上面這行代碼後的情緒,來一副match表達式消消火。
var result = x match( case Func<int> f: f(), case int i: i, case *: default(int) );
這兩種寫法效果上是等效的,有沒有非常干淨清爽的感覺?寫過match表達式的碼農,應該再也不想回去嵌套 <*>?<*>:<*> 了。 (注:目前這種寫法還未確認,C#7.0發布後可能會有略微變動)
Is表達式
如果說上面兩個變化是“語法糖”,那麼is表達式可是要玩真的了。
說點題外話,其實對正則表達式熟悉的童鞋可能知道,本質上[模式匹配]和正則表達式要解決的問題邏輯類似,以一個確定的模式,來判斷或查找一個確定的實例。只不過在正則表達式中,這裡說的"模式"是正則表達式,"實例"指字符串。而[模式匹配]下,所針對的"實例"是對象,那麼"模式",就可以理解成is表達式了。
舉個例子,比如你要查找並列出 一組電子設備中,所有iPhone的IMEI串號,我們在C#6.0中,會這樣做:
1 class Device 2 { 3 public ProductLineOption ProductLine { get; set; } 4 } 5 6 class MobiePhone : Device 7 { 8 public string IMEICode { get; set; } 9 } 10 11 IEnumerable<Device> GetAllDevices() { /* 獲取並返回所有設備 */ }; 12 13 IEnumerable<string> GetAlliPhoneIMEI() 14 { 15 var deviceList = this.GetAllDevices(); 16 foreach (Device device in deviceList) 17 { 18 MobiePhone phone = device as MobiePhone; 19 if (phone == null) continue; 20 21 if (phone.ProductLine == ProductLineOption.IPhone) 22 { 23 yield return phone.IMEICode; 24 } 25 } 26 }
一個非常典型的傳統方法,沒什麼好說的。我們直接來看C#7.0 中 is表達式怎麼等效的實現這段邏輯:
1 IEnumerable<string> GetAlliPhoneIMEI() 2 { 3 List<Device> deviceList = this.GetAllDevices(); 4 foreach (Device device in deviceList) 5 { 6 if (device is MobiePhone { IMEICode is var imei, ProductLine is ProductLineOption.IPhone}) 7 { 8 yield return imei; 9 } 10 } 11 }
如果你還是覺得這沒什麼,那麼,其實這個例子中,僅僅體現出模式匹配中的屬性模式。
根據Doc:#patterns C#7.0會提供一下幾種匹配方式:
我們可以想象,如果模式匹配組合起來使用,會給現有的C#代碼打來多大的便利和清靜。
Okay,說了這麼多,下面給大家一個相對完整的案例,自行體會。
案例
1 abstract class Animal 2 { 3 public string Name { get; set; } 4 } 5 6 class Dog : Animal 7 { 8 public string BarkLikeCrazy() => "WOOF WOOF WOOF"; 9 } 10 11 class Cat : Animal { } 12 class Swan : Animal { } 13 14 class Program 15 { 16 static void Main(string[] args) 17 { 18 var animals = new Animal[] { 19 new Dog { Name = "hola" }, 20 new Cat { Name = "tom" }, 21 new Swan { Name = "hacienda" } 22 }; 23 24 var organizedAnimals = from animal in animals 25 let sound = animal match( //Match語句 26 case Dog d: "woof... " + d.BarkLikeCrazy(), //類型匹配 27 case Cat c: "meow", 28 case * : "I'm mute.." //通配符匹配 29 ) 30 select new { Type = animal, Sound = sound }; 31 32 foreach (var animal in organizedAnimals) 33 { 34 Console.WriteLine($"{animal.Type.ToString()} - {animal.Sound}"); 35 } 36 37 foreach (var a in animals) 38 { 39 if (a is Cat { Name is var name }) //類型及屬性匹配,is表達式 40 { 41 Console.WriteLine($"Name of {nameof(Cat)} is {name}"); 42 } 43 44 string sound = ""; 45 switch (a) //匹配switch語句 46 { 47 case Dog d when d.Name == "hola": 48 sound = "woof... hola" + d.BarkLikeCrazy(); 49 break; 50 case Dog d: 51 sound = "woof..." + d.BarkLikeCrazy(); 52 break; 53 case Cat c: 54 sound = "meow"; 55 break; 56 case IEnumerable<Animal> l when l.Any(): 57 //TODO: any logic; 58 break; 59 case null: 60 sound = "no animal"; 61 break; 62 default: 63 sound = "I'm mute.."; 64 break; 65 } 66 Console.WriteLine($"{a.ToString()} - {sound}"); 67 } 68 } 69 }
注1:模式匹配的部分高級feature,已經確認在C#7.0中移除,可能出現在後續C#版本中。(#10888)。
注2:目前(2016-06-15)VS15的最新Preview下,模式匹配的部分語法依然無法使用。
注3:由於目前仍然未在Roslyn中Release,後期有變動的可能,本文中涉及的樣例代碼以Mads Torgersen在#Build 2016上的演示的語法為准,本文涉及的案例有可能無法在VS15 RTM後正常使用,僅供參考。
(當然,如果筆者樂意,會及時把後期得到確認的變更更新到本文中 ^_^!)
本文鏈接:http://www.cnblogs.com/ylvict/p/5588613.html (轉載請注明)
目前(2016年6月)C#7.0還未正式發布,大家如果想體驗部分特性,可以去下載VS15預覽版,最終發布的語法可能和本文中提及的有所不同,最新動態請大家關注Roslyn項目。