2021年4月19日微軟釋出公告稱將於今年夏季釋出首款64位的 Visual Studio 2022,2021年5月20日又釋出了 Visual Studio 2022 線路圖,進一步提升開發生產力。與 Visual Studio 黃金搭檔的 C# 語言一直都是秒天秒地秒空氣的存在。C#10,今天它來了。

  本週早些時候(2021年5月1日),我關注了Mads TorgersenDotNet SouthWest的一次演講,他是微軟C#語言的首席設計師。他概述了C#10將包含的新酷功能。下面我們一起快速預覽。

小小的免責宣告,這些變化大多數幾乎都完成了。由於它仍在積極開發中,我不能保證一切都與 C# 10 釋出時完全一樣。

record struct 

他首先談到的是 record 的當前實現是使用類(reference type)作為基物件的。C#10中即將提供一個 record struct,它的基礎型別可以是值型別。不同之處在於,常規 record 將通過引用從一個函式傳遞到另一個函式,而 record struct 將通過其值進行復制。record struct  也將支援 with 表示式。

同時,還可以向 record 中新增運算子。這兩種 record 型別都可以使用。

  1. record Person(string Name, string Email)
  2. {
  3. public static Person operator +(Person first, Person second)
  4. {
  5. // TODO 業務邏輯
  6. }
  7. }
required 特性

  C# 團隊關注的目標之一是使物件的初始化更容易。這就是為什麼可以根據需要對 classstructrecord 或 record struct 新增 required 特性標記。它強制要求這些屬性必須賦值。這可以通過建構函式來完成,或者可以通過物件初始化來完成。下面的兩個類定義是等效的。如果用required關鍵字寫的話,不設定Name屬性就不能例項化Person 。編譯器會丟擲錯誤並且無法編譯。

  1. class Person
  2. {
  3. public required string Name { get; set; }
  4. public DateTime DateOfBirth { get; set; }
  5. }
  6.  
  7. class Person
  8. {
  9. public Person(string name) => Name = name;
  10.  
  11. public string Name { get; set; }
  12. public DateTime DateOfBirth { get; set; }
  13. }
field 特性

為了進一步改善屬性(properties),可以完全擺脫 backing field。新的關鍵字 field 將提供對所述支援欄位的訪問。它對 setter 和 init only 屬性都可以使用。

  1. class Person
  2. {
  3. public string Name { get; init => field = value.Trim(); }
  4. public DateTime DateOfBirth { get; set => field = value.Date; }
  5. }
with 表示式

  在下一個版本中也會有一些漂亮的小改進。其中之一就是 with 操作符也將支援匿名型別。

  1. var foo = new
  2. {
  3. Name = "Foo",
  4. Email = "foo@mail.com"
  5. };
  6. var bar = foo with {Name = "Bar"};
namespace 名稱空間

  現在可以建立一個檔案,其中的名稱空間匯入可以在任何地方使用。例如,如果在幾乎每個檔案中都使用了一個常用的名稱空間,例如Microsoft.Extensions.Logging.ILogger,那麼就可以將全域性名稱空間 using Microsoft.Extensions.Logging.ILogger 新增到任何.cs檔案中(我建議使用Program.cs或專用Imports.cs),整個專案中都可以使用 logger 介面。但是,該方法不適用於整個解決方案(solution)。因為沒有人能預測哪些地方需要匯入,所以它們是按專案分組到每個專案(project)中。

  隨後,還會對 namespace 進行優化。現在  namespace 需要花括號 {} 來對程式碼進行分組,這意味著所有程式碼至少要縮排一次。為了節省 tab(或四個空格)和螢幕空間,在檔案中的任何位置新增一個 namespace,將使所有程式碼都屬於該namespace。有相關研究表明絕大多數情況下,一個檔案中的幾乎所有程式碼都屬於同一個 namespace。使用該方案優化後,檔案大小會減小,這對於一個解決方案(即使它包含數千個檔案)來說可能並不重要,但在 GitHub/GitLab/BitBucket/...的規模上,我認為這將為他們節省一些空間。如果有人仍想在一個檔案中包含多個名稱空間,則仍然可以選擇使用大括號。

  1. // 傳統方式 LegacyNamespace.cs
  2. namespace LegacyNamespace
  3. {
  4. class Foo
  5. {
  6. // ToDo 業務邏輯
  7. }
  8. }
  9.  
  10. // 簡化後的方式 SimplifiedNamespace.cs
  11. namespace SimplifiedNamespace;
  12. class Bar
  13. {
  14. // ToDo 業務邏輯
  15. }
lambda 表示式

lambda 語句也有一些很酷的更新。編譯器將更好地支援推斷 lambda 簽名,並且還可以新增屬性。可以指定顯式返回型別以幫助編譯器理解 lambda。

  1. var f = Console.WriteLine;
  2. var f = x => x; // 推斷返回型別
  3. var f = (string x) => x; // 推斷簽名
  4. var f = [NotNull] x => x; // 在屬性上新增特性
  5. var f = [NotNull] (int x) => x;
  6. var f = [return: NotNull] static x => x; // 為返回型別新增特性
  7. var f = T () => default; // 顯示返回型別
  8. var f = ref int (ref int x) => ref x; // 在 struct 上使用 ref 關鍵字
  9. var f = int (x) => x; // 顯式指定隱式輸入的返回型別
  10. var f = static void (_) => Console.Write("Help");
interface介面

  最後,可以在介面上指定靜態方法和屬性。我知道這將是一個有爭議的話題,就像向介面新增預設實現一樣。雖然我不喜歡它,然而這可能非常有趣。想象一下,您可以指定介面的預設值或指定建立方法。

  1. interface IFoo
  2. {
  3. static IFoo Empty { get; }
  4. static operator +(IFoo first, IFoo second);
  5. }

  6. class Foo : IFoo
  7. {
  8. public static IFoo Empty => new Foo();
  9. public static operator +(IFoo first, IFoo second) => /* 在此做邏輯計算 */;
  10. }

就個人而言,我喜歡這些變化。尤其是 namespace 和 interface 的變化和改進。不管怎樣,C#的未來是光明的。


參考文獻:

  • https://kenbonny.net/introducing-csharp-10