標籤:ring argument 理解 write 技術 使用情境 變數賦值 http www
1. out 變數(out variables)
以前我們使用out變數必須在使用前進行聲明,C# 7.0 給我們提供了一種更簡潔的文法 “使用時進行內聯聲明” 。如下所示:
1 var input = ReadLine();2 if (int.TryParse(input, out var result))3 {4 WriteLine("您輸入的數字是:{0}",result);5 }6 else7 {8 WriteLine("無法解析輸入...");9 }
上面代碼編譯後:
1 int num; 2 string s = Console.ReadLine(); 3 if (int.TryParse(s, out num)) 4 { 5 Console.WriteLine("您輸入的數字是:{0}", num); 6 } 7 else 8 { 9 Console.WriteLine("無法解析輸入...");10 }
原理解析:所謂的 “內聯聲明” 編譯後就是以前的原始寫法,只是現在由編譯器來完成。
備忘:在進行內聯聲明時,即可直接寫明變數的類型也可以寫隱式類型,因為out關鍵字修飾的一定是局部變數。
2. 元組(Tuples)
元組(Tuple)在 .Net 4.0 的時候就有了,但元組也有些缺點,如:
1)Tuple 會影響代碼的可讀性,因為它的屬性名稱都是:Item1,Item2.. 。
2)Tuple 還不夠輕量級,因為它是參考型別(Class)。
備忘:上述所指 Tuple 還不夠輕量級,是從某種意義上來說的或者是一種假設,即假設分配操作非常的多。
C# 7 中的元組(ValueTuple)解決了上述兩個缺點:
1)ValueTuple 支援語義上的欄位命名。
2)ValueTuple 是實值型別(Struct)。
1. 如何建立一個元組?
1 var tuple = (1, 2); // 使用文法糖建立元組2 var tuple2 = ValueTuple.Create(1, 2); // 使用靜態方法【Create】建立元組3 var tuple3 = new ValueTuple<int, int>(1, 2); // 使用 new 運算子建立元組4 5 WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三種方式都是等價的。");
原理解析:上面三種方式最終都是使用 new 運算子來建立執行個體。
2. 如何建立給欄位命名的元組?
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 // 左右兩邊同時指定欄位名稱10 (int one, int two) tuple3 = (first: 1, second: 2); /* 此處會有警告:由於目標類型(xx)已指定了其它名稱,因為忽略元組名稱xxx */11 WriteLine($"first:{tuple3.one}, second:{tuple3.two}");
註:左右兩邊同時指定欄位名稱,會使用左邊的欄位名稱覆蓋右邊的欄位名稱(一一對應)。
原理解析:上述給欄位命名的元組在編譯後其欄位名稱還是:Item1, Item2...,即:“命名”只是語義上的命名。
3. 什麼是解構?
解構顧名思義就是將整體分解成部分。
4. 解構元組,如下所示:
1 var (one, two) = GetTuple();2 3 WriteLine($"first:{one}, second:{two}");
1 static (int, int) GetTuple()2 {3 return (1, 2);4 }
原理解析:解構元組就是將元組中的欄位值賦值給聲明的局部變數(編譯後可查看)。
備忘:在解構時“=”左邊能提取變數的資料類型(如上所示),元組中欄位類型相同時即可提取具體類型也可以是隱式類型,但元組中欄位類型
不相同時只能提取隱式類型。
5. 解構可以應用於 .Net 的任意類型,但需要編寫 Deconstruct 方法成員(執行個體或擴充)。如下所示:
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}");
原理解析:編譯後就是由其執行個體調用 Deconstruct 方法,然後給局部變數賦值。
Deconstruct 方法簽名:
1 // 執行個體簽名2 public void Deconstruct(out type variable1, out type variable2...)3 4 // 擴充簽名5 public static void Deconstruct(this type instance, out type variable1, out type variable2...)
總結:1. 元組的原理是利用了成員類型的嵌套或者是說成員類型的遞迴。2. 編譯器很牛B才能提供如此優美的文法。
使用 ValueTuple 則需要匯入: Install - Package System.ValueTuple
3. 模式比對(Pattern matching)
1. is 運算式(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 expressions13 {14 sum += val;15 }16 else if (item is string str && int.TryParse(str, out var result)) // is expressions 和 out variables 結合使用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 ,這個擴充的 is 其實是 as 和 if 的組合。即它先進行 as 轉換再進行 if 判斷,判斷其結果是否為 null,不等於 null 則執行
語句塊邏輯,反之不行。由上可知其實C# 7之前我們也可實作類別似的功能,只是寫法上比較繁瑣。
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): // 類型模式比對 + 條件運算式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,編譯後你會發現擴充的 switch 就是 as 、if 、goto 語句的組合體。同 is expressions 一樣,以前我們也能實
現只是寫法比較繁瑣並且可讀性不強。
總結:模式比對文法是想讓我們在簡單的情況下實作類別似與多態一樣的動態調用,即在運行時確定成員類型和調用具體的實現。
4. 局部引用和引用返回 (Ref locals and returns)
我們知道 C# 的 ref 和 out 關鍵字是對值傳遞的一個補充,是為了防止實值型別大對象在Copy過程中損失更多的效能。現在在C# 7中 ref 關鍵字得
到了加強,它不僅可以擷取實值型別的引用而且還可以擷取某個變數(參考型別)的局部引用。如:
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) 聲明方法簽名時必須在傳回型別前加上 ref 修飾。
b) 在每個 return 關鍵字後也要加上 ref 修飾,以表明是返回引用。
2. 分配引用(即賦值),必須在聲明局部變數前加上 ref 修飾,以及在方法返回引用前加上 ref 修飾。
註:C# 開發的是Managed 程式碼,所以一般不希望程式員去操作指標。並由上述可知在使用過程中需要大量的使用 ref 來標明這是引用變數(編譯後其
實沒那麼多),當然這也是為了提高代碼的可讀性。
總結:雖然 C# 7 中提供了局部引用和引用返回,但為了防止濫用所以也有諸多約束,如:
1. 你不能將一個值分配給 ref 變數,如:
1 ref int num = 10; // error:無法使用值初始化按引用變數
2. 你不能返回一個生存期不超過方法範圍的變數引用,如:
1 public ref int GetLocalRef(int num) => ref num; // error: 無法按引用返回參數,因為它不是 ref 或 out 參數
3. ref 不能修飾 “屬性” 和 “索引器”。
1 var list = new List<int>();2 ref var n = ref list.Count; // error: 屬性或索引器不能作為 out 或 ref 參數傳遞
原理解析:非常簡單就是指標傳遞,並且個人覺得此文法的使用情境非常有限,都是用來處理大對象的,目的是減少GC提高效能。
5. 局部函數(Local functions)
C# 7 中的一個功能“局部函數”,如下所示:
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 [資料類型,void] 方法名([參數])2 {3 // Method body;[] 裡面都是可選項4 }
原理解析:局部函數雖然是在其他函數內部聲明,但它編譯後就是一個被 internal 修飾的靜態函數,它是屬於類,至於它為什麼能夠使用上級函
數中的局部變數和參數呢?那是因為編譯器會根據其使用的成員產生一個新類型(Class/Struct)然後將其傳入函數中。由上可知則局部函數的聲
明跟位置無關,並可無限嵌套。
總結:個人覺得局部函數是對 C# 異常機制在語義上的一次補充(如上例),以及為代碼提供清晰的結構而設定的文法。但局部函數也有其缺點,
就是局部函數中的代碼無法複用(反射除外)。
6. 更多的運算式體成員(More expression-bodied members)
C# 6 的時候就支援運算式體成員,但當時只支援“函數成員”和“唯讀屬性”,這一特性在C# 7中得到了擴充,它能支援更多的成員:建構函式
、解構函式、帶 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 Name13 {14 get => _name;15 set => _name = value ?? "Mike";16 }17 18 // Expression-bodied indexers19 public string this[string name] => Convert.ToBase64String(Encoding.UTF8.GetBytes(name));20 }
備忘:索引器其實在C# 6中就得到了支援,但其它三種在C# 6中未得到支援。
7. Throw 運算式(Throw expressions)
異常機制是C#的重要組成部分,但在以前並不是所有語句都可以拋出異常的,如:條件運算式(? :)、null合并運算子(??)、一些Lambda
運算式。而使用 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("參數不合法") : value;11 }12 13 static string GetName() => null;14 }
8. 擴充非同步傳回型別(Generalized async return types)
以前非同步傳回型別必須是:Task、Task<T>、void,現在 C# 7 中新增了一種類型:ValueTask<T>,如下所示:
1 public async ValueTask<int> Func()2 {3 await Task.Delay(3000);4 return 100;5 }
總結:ValueTask<T> 與 ValueTuple 非常相似,所以就不列舉: ValueTask<T> 與 Task 之間的異同了,但它們都是為了最佳化特定情境效能而
新增的類型。
使用 ValueTask<T> 則需要匯入: Install - Package System.Threading.Tasks.Extensions
9. 數字文本文法的改進(Numeric literal syntax improvements)
C# 7 還包含兩個新特性:二進位文字、數字分隔字元,如下所示:
1 var one = 0b0001;2 var sixteen = 0b0001_0000;3 4 long salary = 1000_000_000;5 decimal pi = 3.141_592_653_589m;
註:二進位文本是以0b(零b)開頭,字母不區分大小寫;數字分隔字元只有三個地方不能寫:開頭,結尾,小數點前後。
總結:二進位文本,數字分隔字元 可使常量值更具可讀性。
雨夜瀟湘 QQ群:297829948原文:30分鐘掌握 C#7出處:http://www.cnblogs.com/VVStudy/p/6551300.html本文著作權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文串連。
詳解C#7.0新特性