自動屬性預設初始化
使用方法:
1 |
public string Name { get; set; } = "hello world"; |
為了便於理解使用2.0語法展示,編譯器生成程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class Customer { [CompilerGenerated] private string kBackingField = "<span style="color: #993300;">hello world</span>"; public Customer() { this.kBackingField = "hello world"; } public string Name { [CompilerGenerated] get { return this.<Name>k__BackingField; } [CompilerGenerated] set { this.<Name>k__BackingField = value; } } } |
從生成程式碼中可以看出編譯器是在例項建構函式時,初始化屬性資訊的。
自動只讀屬性預設初始化
使用方法:
1 |
public string Name1 { get; } = "<span style="color: #993300;">hello world</span>"; |
為了便於理解使用2.0語法展示,編譯器生成程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class Customer { [CompilerGenerated] private string kBackingField = "hello world"; public Customer() { this.kBackingField = "hello world"; } public string Name { [CompilerGenerated] get { return this.<Name>k__BackingField; } [CompilerGenerated] set { this.<Name>k__BackingField = value; } } } |
從生成程式碼中可以看出編譯器是在例項建構函式時,初始化屬性資訊的。
自動只讀屬性預設初始化
使用方法:
1 |
public string Name1 { get; } = "<span style="color: #993300;">hello world</span>"; |
編譯器生成程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 |
[CompilerGenerated] private readonly string kBackingField; public Customer() { this.kBackingField = "hello world"; } public string Name1 { [CompilerGenerated] get { return this.k__BackingField; } } |
由於初始化預設值實在建構函式中賦值的,所以跟屬性只讀沒關係。
表示式為主體的函式
使用方法:
1 |
Body Get(int x, int y) => new Body(1 + x, 2 + y); |
編譯器生成如下:
1 2 3 4 |
private Program.Body Get(int x, int y) { return new Program.Body(1 + x, 2 + y); } |
簡化了單行方法的編寫,省去寫大括號的功夫。
同時支援沒有返回值的寫法:
1 |
void OutPut(int x, int y) => Console.WriteLine("hello world"); |
也支援非同步函式的編寫
1 |
async void OutPut(int x, int y) => await new Task(() => Console.WriteLine("hello wolrd")); |
表示式為主體的屬性(賦值)
使用方法:
1 |
public string Name2 => "hello world"; |
編譯器生成程式碼如下
1 2 3 4 |
public string Name2 { get { return "mushroomsir"; } } |
編譯器只生成了個只讀屬性。
靜態類匯入
這個特性可以一次性匯入某型別的所有靜態成員,使靜態成員在後面的程式碼中沒有型別限制直接使用,像使用本型別下面的靜態方法一樣。
1 2 3 4 5 6 7 8 |
using static System.Console; class Program { static void Main(string[] args) { WriteLine("hello wolrd"); } } |
編譯器生成程式碼如下
1 2 3 4 |
private static void Main(string[] args) { Console.WriteLine("hello wolrd"); } |
省去了型別名稱的重複編寫。
Null條件運算子
使用方法:
1 2 3 4 5 |
Customer customer = new Customer(); if (customer1 != null) { string name = customer1.Name; } |
等同於:
1 |
Customer customer = new Customer(); string name3 = customer?.Name; |
可以和??組合起來使用:
1 |
if (customer?.Face2()??false) |
還可以2個一起用:
1 |
int? Length = customer?.Name?.Length; |
也可以方法呼叫:
1 |
customer?.Face(); |
這個語法糖的目的是在物件使用前檢查是否為null。如果物件為空,則賦值給變數為空值,所以例子中需要一個可以為空的int型別、即int?。
如果物件不為空,則呼叫物件的成員取值,並賦值給變數。
字串格式化
String.Format有些不方便的地方是:必須輸入”String.Format”,使用{0}佔位符、必須順序來格式化、這點容易出錯。
1 |
var s = String.Format("{0} is {1} year {{s}} old", p.Name, p.Age); |
新的語法糖使用起來相對更輕鬆些:
1 |
var s = $"{p.Name} is {p.Age} year{{s}} old"; |
編譯器生成如下,和之前沒有區別:
1 |
var s = String.Format("{0} is {1} year{{s}} old", p.Name, p.Age); |
有趣的是,新格式化方式還支援任何表示式的直接賦值:
1 |
var s = $"{p.Name} is {p.Age} year{(p.Age == 1 ? "" : "s")} old"; |
索引初始化
List雖然這樣寫可以編譯通過,但是會拋異常的,使用方法:
1 |
var numbers = new List<string> { [7] = "seven", [9] = "nine", [13] = "thirteen" }; |
編譯器生成程式碼如下:
1 2 3 4 |
List list = new List(); list[7] = "seven"; list[9] = "nine"; list[13] = "thirteen"; |
Dictionary可以執行,因為二者內部索引機制不一樣:
1 |
var numbers = new Dictionary<int, string> {[7] = "seven",[9] = "nine",[13] = "thirteen" }; |
編譯器生成程式碼:
1 2 3 4 5 |
Dictionary<int, string> dictionary2 = new Dictionary<int, string>(); dictionary2[7] = "seven"; dictionary2[9] = "nine"; dictionary2[13] = "thirteen"; Dictionary<int, string> dictionary = dictionary2; |
異常過濾器when
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
try { throw new ArgumentException("string error"); } catch (ArgumentException e) when (myfilter(e)) { Console.WriteLine(e.Message); } static bool myfilter(ArgumentException e) { return false; } |
When語法作用是:在進入到catch之前、驗證when括號裡myfilter方法返回的bool,如果返回true繼續執行,false不走catch直接丟擲異常。
使用這個filter可以更好的判斷一個錯誤是繼續處理還是重新丟擲去。按照以前的做法,在catch塊內如需再次丟擲去,需要重新throw出去,這時的錯誤源是捕捉後在拋的,而不是原先的,有了when語法就可以直接定位到錯誤源。
catch和finally程式碼塊內的Await
Await非同步處理是在c#5.0提出的,但不能在catch和finally程式碼塊內使用,這次在C#6.0更新上支援了。
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
async void Solve() { try { await HttpMethodAsync(); } catch (ArgumentException e) { await HttpMethodAsync(); } finally { await HttpMethodAsync(); } } |
nameof表示式
使用方法:
1 2 |
string name = ""; Console.WriteLine(nameof(name)); |
控制檯輸出 “name”。
有時候會需要程式中一些成員的字串名稱,比如丟擲ArgumentNullException異常的時候,想知道ArgumentNullException型別的字串名稱,這時候就可以用nameof獲取字元
串“ArgumentNullException”。現在做法都是手動複製一下,但重構改名的時候容易忘記變更字串,使用nameof就可以避免了。
當如下使用的時候,編譯器會只取最後的ZipCode。
1 |
nameof(person.Address.ZipCode) |
編譯器生成如下程式碼:
1 |
Console.WriteLine("name"); |
擴充套件方法
1 2 3 4 5 6 7 8 9 10 |
using static System.Linq.Enumerable; //引入型別,而不是名稱空間 class Program { static void Main() { var range = Range(5, 17); // Ok: 不是擴充套件方法 var odd = Where(range, i => i % 2 == 1); // Error, 不在全域性作用域裡 var even = range.Where(i => i % 2 == 0); // Ok } } |
首先Enumerable是個靜態類,裡面是各種擴充套件方法,比如range。static的作用是把型別的靜態成員一次性匯入,rang雖然是靜態方法,但不能匯入,比如where。
因為擴充套件方法雖然是一個靜態方法,但是語法規定它作為一個例項方法使用(打點),所以不能在全域性作用域裡當靜態方法用,因此var odd = Where(range, i => i % 2 == 1)是錯誤的。
但是static卻能把型別的擴充套件方法作為擴充套件方法本身角色的功能匯入進去,所以var even = range.Where(i => i % 2 == 0)是ok的。
這裡可能稍微有點繞,lz儘量寫清楚,static新用法有2個功能:
把靜態成員匯入,但擴充套件方法比較特殊、排除在外。這時static是c# 6.0的新功能。
等同於把擴充套件方法的名稱空間匯入,所以在集合上可以打點呼叫擴充套件方法。這是之前就有的功能,而不是把擴充套件方法轉成單純的靜態方法匯入使用。
總結
看到園子裡有介紹的文章,一時來興趣了,下班後安裝個社群版就研究分享下。 雖然微軟一直出新東西,但都是由下至上迭代的,所以學習起來是非常快的。
參考https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#expression-bodied-function-members