30分钟掌握 C#7
1.?out 變量(out variables)
? 以前我們使用out變量必須在使用前進(jìn)行聲明,C# 7.0 給我們提供了一種更簡(jiǎn)潔的語法 “使用時(shí)進(jìn)行內(nèi)聯(lián)聲明” 。如下所示:
1 var input = ReadLine(); 2 if (int.TryParse(input, out var result)) 3 { 4 WriteLine("您輸入的數(shù)字是:{0}",result); 5 } 6 else 7 { 8 WriteLine("無法解析輸入..."); 9 }? 上面代碼編譯后:
1 int num; 2 string s = Console.ReadLine(); 3 if (int.TryParse(s, out num)) 4 { 5 Console.WriteLine("您輸入的數(shù)字是:{0}", num); 6 } 7 else 8 { 9 Console.WriteLine("無法解析輸入..."); 10 }?原理解析:所謂的 “內(nèi)聯(lián)聲明” 編譯后就是以前的原始寫法,只是現(xiàn)在由編譯器來完成。
?備注:在進(jìn)行內(nèi)聯(lián)聲明時(shí),即可直接寫明變量的類型也可以寫隱式類型,因?yàn)閛ut關(guān)鍵字修飾的一定是局部變量。
2. 元組(Tuples)
?元組(Tuple)在 .Net 4.0 的時(shí)候就有了,但元組也有些缺點(diǎn),如:
? ?1)Tuple 會(huì)影響代碼的可讀性,因?yàn)樗膶傩悦际?#xff1a;Item1,Item2.. 。
? ?2)Tuple 還不夠輕量級(jí),因?yàn)樗且妙愋?#xff08;Class)。
? ?備注:上述所指 Tuple 還不夠輕量級(jí),是從某種意義上來說的或者是一種假設(shè),即假設(shè)分配操作非常的多。
?C# 7 中的元組(ValueTuple)解決了上述兩個(gè)缺點(diǎn):
? ?1)ValueTuple 支持語義上的字段命名。
? ?2)ValueTuple 是值類型(Struct)。
?1. 如何創(chuàng)建一個(gè)元組?
1 var tuple = (1, 2); // 使用語法糖創(chuàng)建元組 2 var tuple2 = ValueTuple.Create(1, 2); // 使用靜態(tài)方法【Create】創(chuàng)建元組 3 var tuple3 = new ValueTuple<int, int>(1, 2); // 使用 new 運(yùn)算符創(chuàng)建元組 4 5 WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三種方式都是等價(jià)的。");?原理解析:上面三種方式最終都是使用 new 運(yùn)算符來創(chuàng)建實(shí)例。
?2. 如何創(chuàng)建給字段命名的元組?
1 // 左邊指定字段名稱 2 (int one, int two) tuple = (1, 2); 3 WriteLine($"first:{tuple.one}, second:{tuple.two}"); 4 5 // 右邊指定字段名稱 6 var tuple2 = (one: 1, two: 2); 7 WriteLine($"first:{tuple2.one}, second:{tuple2.two}"); 8 9 // 左右兩邊同時(shí)指定字段名稱 10 (int one, int two) tuple3 = (first: 1, second: 2); /* 此處會(huì)有警告:由于目標(biāo)類型(xx)已指定了其它名稱,因?yàn)楹雎栽M名稱xxx */ 11 WriteLine($"first:{tuple3.one}, second:{tuple3.two}");?注:左右兩邊同時(shí)指定字段名稱,會(huì)使用左邊的字段名稱覆蓋右邊的字段名稱(一一對(duì)應(yīng))。?
?原理解析:上述給字段命名的元組在編譯后其字段名稱還是:Item1, Item2...,即:“命名”只是語義上的命名。
?3. 什么是解構(gòu)?
?解構(gòu)顧名思義就是將整體分解成部分。
?4. 解構(gòu)元組,如下所示:
1 var (one, two) = GetTuple(); 2 3 WriteLine($"first:{one}, second:{two}"); 1 static (int, int) GetTuple() 2 { 3 return (1, 2); 4 }?原理解析:解構(gòu)元組就是將元組中的字段值賦值給聲明的局部變量(編譯后可查看)。
?備注:在解構(gòu)時(shí)“=”左邊能提取變量的數(shù)據(jù)類型(如上所示),元組中字段類型相同時(shí)即可提取具體類型也可以是隱式類型,但元組中字段類型
?不相同時(shí)只能提取隱式類型。
?5. 解構(gòu)可以應(yīng)用于 .Net 的任意類型,但需要編寫?Deconstruct 方法成員(實(shí)例或擴(kuò)展)。如下所示:
1 public class Student 2 { 3 public Student(string name, int age) 4 { 5 Name = name; 6 Age = age; 7 } 8 9 public string Name { get; set; } 10 11 public int Age { get; set; } 12 13 public void Deconstruct(out string name, out int age) 14 { 15 name = Name; 16 age = Age; 17 } 18 }?使用方式如下:
1 var (Name, Age) = new Student("Mike", 30); 2 3 WriteLine($"name:{Name}, age:{Age}");?原理解析:編譯后就是由其實(shí)例調(diào)用?Deconstruct 方法,然后給局部變量賦值。
?Deconstruct 方法簽名:
1 // 實(shí)例簽名 2 public void Deconstruct(out type variable1, out type variable2...) 3 4 // 擴(kuò)展簽名 5 public static void Deconstruct(this type instance, out type variable1, out type variable2...)?總結(jié):1. 元組的原理是利用了成員類型的嵌套或者是說成員類型的遞歸。2. 編譯器很牛B才能提供如此優(yōu)美的語法。
使用 ValueTuple 則需要導(dǎo)入: Install - Package System.ValueTuple3.?模式匹配(Pattern matching)
?1. is 表達(dá)式(is expressions),如:
1 static int GetSum(IEnumerable<object> values) 2 { 3 var sum = 0; 4 if (values == null) return sum; 5 6 foreach (var item in values) 7 { 8 if (item is short) // C# 7 之前的 is expressions 9 { 10 sum += (short)item; 11 } 12 else if (item is int val) // C# 7 的 is expressions 13 { 14 sum += val; 15 } 16 else if (item is string str && int.TryParse(str, out var result)) // is expressions 和 out variables 結(jié)合使用 17 { 18 sum += result; 19 } 20 else if (item is IEnumerable<object> subList) 21 { 22 sum += GetSum(subList); 23 } 24 } 25 26 return sum; 27 }?使用方法:
1 條件控制語句(obj is type variable) 2 { 3 // Processing... 4 }?原理解析:此 is 非彼 is ,這個(gè)擴(kuò)展的 is 其實(shí)是 as 和 if 的組合。即它先進(jìn)行 as 轉(zhuǎn)換再進(jìn)行 if 判斷,判斷其結(jié)果是否為 null,不等于 null 則執(zhí)行
?語句塊邏輯,反之不行。由上可知其實(shí)C# 7之前我們也可實(shí)現(xiàn)類似的功能,只是寫法上比較繁瑣。
?2. switch語句更新(switch statement updates),如:
1 static int GetSum(IEnumerable<object> values) 2 { 3 var sum = 0; 4 if (values == null) return 0; 5 6 foreach (var item in values) 7 { 8 switch (item) 9 { 10 case 0: // 常量模式匹配 11 break; 12 case short sval: // 類型模式匹配 13 sum += sval; 14 break; 15 case int ival: 16 sum += ival; 17 break; 18 case string str when int.TryParse(str, out var result): // 類型模式匹配 + 條件表達(dá)式 19 sum += result; 20 break; 21 case IEnumerable<object> subList when subList.Any(): 22 sum += GetSum(subList); 23 break; 24 default: 25 throw new InvalidOperationException("未知的類型"); 26 } 27 } 28 29 return sum; 30 }?使用方法:
1 switch (item) 2 { 3 case type variable1: 4 // processing... 5 break; 6 case type variable2 when predicate: 7 // processing... 8 break; 9 default: 10 // processing... 11 break; 12 }?原理解析:此 switch 非彼 switch,編譯后你會(huì)發(fā)現(xiàn)擴(kuò)展的 switch 就是 as 、if 、goto 語句的組合體。同 is?expressions 一樣,以前我們也能實(shí)
?現(xiàn)只是寫法比較繁瑣并且可讀性不強(qiáng)。
?總結(jié):模式匹配語法是想讓我們?cè)诤?jiǎn)單的情況下實(shí)現(xiàn)類似與多態(tài)一樣的動(dòng)態(tài)調(diào)用,即在運(yùn)行時(shí)確定成員類型和調(diào)用具體的實(shí)現(xiàn)。
4. 局部引用和引用返回 (Ref locals and returns)
?我們知道 C# 的 ref 和 out 關(guān)鍵字是對(duì)值傳遞的一個(gè)補(bǔ)充,是為了防止值類型大對(duì)象在Copy過程中損失更多的性能。現(xiàn)在在C# 7中?ref 關(guān)鍵字得
?到了加強(qiáng),它不僅可以獲取值類型的引用而且還可以獲取某個(gè)變量(引用類型)的局部引用。如:
1 static ref int GetLocalRef(int[,] arr, Func<int, bool> func) 2 { 3 for (int i = 0; i < arr.GetLength(0); i++) 4 { 5 for (int j = 0; j < arr.GetLength(1); j++) 6 { 7 if (func(arr[i, j])) 8 { 9 return ref arr[i, j]; 10 } 11 } 12 } 13 14 throw new InvalidOperationException("Not found"); 15 }?Call:
1 int[,] arr = { { 10, 15 }, { 20, 25 } }; 2 ref var num = ref GetLocalRef(arr, c => c == 20); 3 num = 600; 4 5 Console.WriteLine(arr[1, 0]);?Print results:
?
?使用方法:
?1. 方法的返回值必須是引用返回:
? ? ?a) ?聲明方法簽名時(shí)必須在返回類型前加上 ref 修飾。
? ? ?b) ?在每個(gè) return 關(guān)鍵字后也要加上 ref 修飾,以表明是返回引用。
?2. 分配引用(即賦值),必須在聲明局部變量前加上 ref 修飾,以及在方法返回引用前加上 ref 修飾。
?注:C# 開發(fā)的是托管代碼,所以一般不希望程序員去操作指針。并由上述可知在使用過程中需要大量的使用 ref 來標(biāo)明這是引用變量(編譯后其
?實(shí)沒那么多),當(dāng)然這也是為了提高代碼的可讀性。?
?總結(jié):雖然 C# 7 中提供了局部引用和引用返回,但為了防止濫用所以也有諸多約束,如:
?1. 你不能將一個(gè)值分配給 ref 變量,如:
1 ref int num = 10; // error:無法使用值初始化按引用變量?2. 你不能返回一個(gè)生存期不超過方法作用域的變量引用,如:
1 public ref int GetLocalRef(int num) => ref num; // error: 無法按引用返回參數(shù),因?yàn)樗皇?ref 或 out 參數(shù)?3. ref 不能修飾 “屬性” 和 “索引器”。?
1 var list = new List<int>(); 2 ref var n = ref list.Count; // error: 屬性或索引器不能作為 out 或 ref 參數(shù)傳遞?原理解析:非常簡(jiǎn)單就是指針傳遞,并且個(gè)人覺得此語法的使用場(chǎng)景非常有限,都是用來處理大對(duì)象的,目的是減少GC提高性能。
5.?局部函數(shù)(Local functions)
?C# 7 中的一個(gè)功能“局部函數(shù)”,如下所示:
1 static IEnumerable<char> GetCharList(string str) 2 { 3 if (IsNullOrWhiteSpace(str)) 4 throw new ArgumentNullException(nameof(str)); 5 6 return GetList(); 7 8 IEnumerable<char> GetList() 9 { 10 for (int i = 0; i < str.Length; i++) 11 { 12 yield return str[i]; 13 } 14 } 15 }?使用方法:
1 [數(shù)據(jù)類型,void] 方法名([參數(shù)]) 2 { 3 // Method body;[] 里面都是可選項(xiàng) 4 }?原理解析:局部函數(shù)雖然是在其他函數(shù)內(nèi)部聲明,但它編譯后就是一個(gè)被 internal 修飾的靜態(tài)函數(shù),它是屬于類,至于它為什么能夠使用上級(jí)函
?數(shù)中的局部變量和參數(shù)呢?那是因?yàn)榫幾g器會(huì)根據(jù)其使用的成員生成一個(gè)新類型(Class/Struct)然后將其傳入函數(shù)中。由上可知?jiǎng)t局部函數(shù)的聲
?明跟位置無關(guān),并可無限嵌套。
?總結(jié):個(gè)人覺得局部函數(shù)是對(duì) C# 異常機(jī)制在語義上的一次補(bǔ)充(如上例),以及為代碼提供清晰的結(jié)構(gòu)而設(shè)置的語法。但局部函數(shù)也有其缺點(diǎn),
?就是局部函數(shù)中的代碼無法復(fù)用(反射除外)。
6. 更多的表達(dá)式體成員(More expression-bodied members)
?C# 6 的時(shí)候就支持表達(dá)式體成員,但當(dāng)時(shí)只支持“函數(shù)成員”和“只讀屬性”,這一特性在C# 7中得到了擴(kuò)展,它能支持更多的成員:構(gòu)造函數(shù)
?、析構(gòu)函數(shù)、帶 get,set 訪問器的屬性、以及索引器。如下所示:
1 public class Student 2 { 3 private string _name; 4 5 // Expression-bodied constructor 6 public Student(string name) => _name = name; 7 8 // Expression-bodied finalizer 9 ~Student() => Console.WriteLine("Finalized!"); 10 11 // Expression-bodied get / set accessors. 12 public string Name 13 { 14 get => _name; 15 set => _name = value ?? "Mike"; 16 } 17 18 // Expression-bodied indexers 19 public string this[string name] => Convert.ToBase64String(Encoding.UTF8.GetBytes(name)); 20 }?備注:索引器其實(shí)在C# 6中就得到了支持,但其它三種在C# 6中未得到支持。
7. Throw 表達(dá)式(Throw expressions)?
?異常機(jī)制是C#的重要組成部分,但在以前并不是所有語句都可以拋出異常的,如:條件表達(dá)式(? :)、null合并運(yùn)算符(??)、一些Lambda
?表達(dá)式。而使用 C# 7 您可在任意地方拋出異常。如:
1 public class Student 2 { 3 private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName)); 4 5 private int _age; 6 7 public int Age 8 { 9 get => _age; 10 set => _age = value <= 0 || value >= 130 ? throw new ArgumentException("參數(shù)不合法") : value; 11 } 12 13 static string GetName() => null; 14 }8. 擴(kuò)展異步返回類型(Generalized async return types)?
?以前異步的返回類型必須是:Task、Task<T>、void,現(xiàn)在 C# 7 中新增了一種類型:ValueTask<T>,如下所示:
1 public async ValueTask<int> Func() 2 { 3 await Task.Delay(3000); 4 return 100; 5 }?總結(jié):ValueTask<T> 與 ValueTuple 非常相似,所以就不列舉: ValueTask<T> 與 Task 之間的異同了,但它們都是為了優(yōu)化特定場(chǎng)景性能而
?新增的類型。
使用 ValueTask<T> 則需要導(dǎo)入: Install - Package System.Threading.Tasks.Extensions9. 數(shù)字文本語法的改進(jìn)(Numeric literal syntax improvements)?
?C# 7 還包含兩個(gè)新特性:二進(jìn)制文字、數(shù)字分隔符,如下所示:
1 var one = 0b0001; 2 var sixteen = 0b0001_0000; 3 4 long salary = 1000_000_000; 5 decimal pi = 3.141_592_653_589m;?注:二進(jìn)制文本是以0b(零b)開頭,字母不區(qū)分大小寫;數(shù)字分隔符只有三個(gè)地方不能寫:開頭,結(jié)尾,小數(shù)點(diǎn)前后。
?總結(jié):二進(jìn)制文本,數(shù)字分隔符 可使常量值更具可讀性。
轉(zhuǎn)載于:https://www.cnblogs.com/VVStudy/p/6551300.html
總結(jié)
以上是生活随笔為你收集整理的30分钟掌握 C#7的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 量子化信息素蚁群优化特征选择算法
- 下一篇: 算法整理学习(二)