C# 6.0 版本包含許多可提升開發人員工做效率的功能。 這些功能的整體效果是讓你編寫的代碼更簡潔、更具可讀性。 該語法不像許多常見作法那樣繁瑣。 能夠更輕鬆地看出設計意圖。 好好了解這些功能能夠幫助你提升生產力,編寫更具可讀性的代碼。 你能夠更專一於功能,而不是語言的構造。html
本文的其他部分是對每一個功能的概述,並提供用於探索每一個功能的連接。 還能夠在教程部分的 C# 6 交互式探索中探索這些功能。web
public string FirstName { get; } public string LastName { get; }
FirstName
和 LastName
屬性只能在構造函數的主體中設置;express
嘗試在另外一種普通方法中設置 LastName
會生成 CS0200
編譯錯誤:api
此功能實現用於建立不可變類型的真正語言支持且使用更簡潔和方便的自動屬性語法。數組
public class Student { public string FirstName { get; } = "張"; public string LastName { get; private set; } = "傳寧"; }
FirstName,LaseName
成員在聲明它的位置處被初始化。 這樣,就能更容易地僅執行一次初始化。 初始化是屬性聲明的一部分,可更輕鬆地將存儲分配。安全
ToString()
一般是理想之選:
public override string ToString() => $"{LastName}, {FirstName}";
也能夠將此語法用於只讀屬性:app
public string FullName => $"{FirstName} {LastName}";
將現有成員更改成 expression bodied 成員是二進制兼容的更改。async
using static System.Math;
Math 不包含任何實例方法。 還可使用 using static
爲具備靜態和實例方法的類導入類的靜態方法。 最有用的示例之一是 String:ide
using static System.String;
在 using static 語句中必須使用徹底限定的類名 System.String。 而不能使用 string 關鍵字。函數
從 static using
語句導入時,僅在使用擴展方法調用語法調用擴展方法時,擴展方法纔在範圍內。 做爲靜態方法調用時,擴展方法不在範圍內。 你在 LINQ 查詢中會常常看到這種狀況。 能夠經過導入 Enumerable 或 Queryable 來導入 LINQ 模式。
using static System.Linq.Enumerable;
一般使用擴展方法調用表達式調用擴展方法。 在使用靜態方法調用語法對其進行調用的罕見狀況下,添加類名稱能夠解決歧義。
static using
指令還能夠導入任何嵌套的類型。 能夠引用任何嵌套的類型,而無需限定。
看下面的一個具體事例:
舊語法:
using System; namespace Demo002_NF46_CS60 { class Program { static void Main(string[] args) { Console.WriteLine("Hello world"); } } }
引入 using static 語法:
using static System.Console; namespace Demo002_NF46_CS60 { class Program { static void Main(string[] args) { WriteLine("Hello world"); } } }
關於using static 的更具體的信息,請參考《using 靜態指令》
.
替換爲 ?.
:
var first = person?.FirstName;
在前面的示例中,若是 Person 對象是 null
,則將變量 first
賦值爲 null
。 不然,將 FirstName
屬性的值分配給該變量。 最重要的是?.
意味着當 person
變量爲 null
時,此行代碼不會生成 NullReferenceException
。 它會短路並返回 null
。 還能夠將 null 條件運算符用於數組或索引器訪問。 將索引表達式中的 []
替換爲 ?[]
。
當 FirstName 爲 null 時,變量 firstName 爲 null,打印輸出時不報錯:
不管 person
的值是什麼,如下表達式均返回 string
。 一般,將此構造與「null 合併」運算符一塊兒使用,以在其中一個屬性爲 null
時分配默認值。 表達式短路時,鍵入返回的 null
值以匹配整個表達式。
first = person?.FirstName ?? "Unspecified";
還能夠將 ?.
用於有條件地調用方法。 具備 null 條件運算符的成員函數的最多見用法是用於安全地調用可能爲 null
的委託(或事件處理程序)。 經過使用 ?.
運算符調用該委託的 Invoke
方法來訪問成員。 能夠在委託模式一文中看到示例。
?.
運算符的規則確保運算符的左側僅計算一次。 它支持許多語法,包括使用事件處理程序的如下示例:
// preferred in C# 6: this.SomethingHappened?.Invoke(this, eventArgs);
確保左側只計算一次,這使得你能夠在 ?.
的左側使用任何表達式(包括方法調用)。
$
做爲字符串的開頭,並使用 {
和 }
之間的表達式代替序號:
public string FullName => $"{FirstName} {LastName}";
本示例使用替表明達式的屬性。 可使用任何表達式。 例如,能夠在內插過程當中計算學生的成績平均值:
public string GetGradePointPercentage() => $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";
上一行代碼將 Grades.Average()
的值格式設置爲具備兩位小數的浮點數。
一般,可能須要使用特定區域性設置生成的字符串的格式。 請利用經過字符串內插生成的對象能夠隱式轉換爲 System.FormattableString 這一事實。 FormattableString 實例包含組合格式字符串,以及在將其轉換爲字符串以前評估表達式的結果。 在設置字符串的格式時,可使用 FormattableString.ToString(IFormatProvider) 方法指定區域性。 下面的示例使用德語 (de-DE) 區域性生成字符串。 (德語區域性默認使用「,」字符做爲小數分隔符,使用「.」字符做爲千位分隔符。)
FormattableString str = $"Average grade is {s.Grades.Average()}"; var gradeStr = str.ToString(new System.Globalization.CultureInfo("de-DE"));
true
,則 catch 子句將對異常執行正常處理。 若是表達式計算結果爲 false
,則將跳過 catch
子句。 一種用途是檢查有關異常的信息,以肯定 catch
子句是否能夠處理該異常:
public static async Task<string> MakeRequest() { WebRequestHandler webRequestHandler = new WebRequestHandler(); webRequestHandler.AllowAutoRedirect = false; using (HttpClient client = new HttpClient(webRequestHandler)) { var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/"); try { var responseText = await stringTask; return responseText; } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301")) { return "Site Moved"; } } }
至關於
catch (System.Net.Http.HttpRequestException e) { if(e.Message.Contains("301")) // 若是判斷的邏輯較多,建議使用該方式。 { return "Site Moved"; } }
nameof
表達式的計算結果爲符號的名稱。 每當須要變量、屬性或成員字段的名稱時,這是讓工具正常運行的好辦法。 nameof
的其中一個最多見的用途是提供引發異常的符號的名稱:
if (IsNullOrWhiteSpace(lastName)) { throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName)); }
另外一個用途是用於實現 INotifyPropertyChanged
接口的基於 XAML 的應用程序:
private string lastName; public string LastName { get { return lastName; } set { if (value != lastName) { lastName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LastName))); } } }
await
表達式的位置有若干限制。 使用 C# 6,如今能夠在 catch
或 finally
表達式中使用 await
。 這一般用於日誌記錄方案:
public static async Task<string> MakeRequestAndLogFailures() { await logMethodEntrance(); var client = new System.Net.Http.HttpClient(); var streamTask = client.GetStringAsync("https://localHost:10000"); try
{ var responseText = await streamTask; return responseText; } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301")) { await logError("Recovered from redirect", e); return "Site Moved"; } finally { await logMethodExit(); client.Dispose(); } }
在 catch
和 finally
子句中添加 await
支持的實現細節可確保該行爲與同步代碼的行爲一致。 當在 catch
或 finally
子句中執行的代碼引起異常時,執行將在下一個外層塊中查找合適的 catch
子句。 若是存在當前異常,則該異常將丟失。 catch
和 finally
子句中的 awaited 表達式也會發生一樣的狀況:搜索合適的 catch
,而且當前異常(若是有)將丟失。
鑑於此行爲,建議仔細編寫 catch 和 finally 子句,避免引入新的異常。
private Dictionary<int, string> messages = new Dictionary<int, string> { { 404, "Page not Found"}, { 302, "Page moved, but left a forwarding address."}, { 500, "The web server can't come out to play today."} };
能夠將集合初始值設定項與 Dictionary<TKey,TValue> 集合和其餘類型一塊兒使用,在這種狀況下,可訪問的 Add
方法接受多個參數。 新語法支持使用索引分配到集合中:
private Dictionary<int, string> webErrors = new Dictionary<int, string> { [404] = "Page not Found", [302] = "Page moved, but left a forwarding address.", [500] = "The web server can't come out to play today." };
此功能意味着,可使用與多個版本中已有的序列容器語法相似的語法初始化關聯容器。
Add
方法使用擴展方法。 添加此功能的目的是進行 Visual Basic 的奇偶校驗。 若是自定義集合類的方法具備經過語義方式添加新項的名稱,則此功能很是有用。
static Task DoThings() { return Task.FromResult(0); }
在早期版本的 C# 中,使用方法組語法調用該方法將失敗:
Task.Run(DoThings);
早期的編譯器沒法正確區分 Task.Run(Action)
和 Task.Run(Func<Task>())
。 在早期版本中,須要使用 lambda 表達式做爲參數:
Task.Run(() => DoThings());
C# 6 編譯器正確地肯定 Task.Run(Func<Task>())
是更好的選擇。
-deterministic
選項指示編譯器爲同一源文件的後續編譯生成徹底相同的輸出程序集。
默認狀況下,每一個編譯都生成惟一的輸出內容。 編譯器添加一個時間戳和一個隨機生成的 GUID。 若是想按字節比較輸出以確保各項生成之間的一致性,請使用此選項。
有關詳細信息,請參閱 -deterministic 編譯器選項文章。