.NET6系列:C#10新功能預覽

  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 類型都可以使用。

record Person(string Name, string Email)
{
  public static Person operator +(Person first, Person second)
  {
     // TODO 業務邏輯
  }
}
required 特性

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

class Person
{
  public required string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
}

class Person
{
  public Person(string name) => Name = name;

  public string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
}
field 特性

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

class Person
{
  public string Name { get; init => field = value.Trim(); }
  public DateTime DateOfBirth { get; set => field = value.Date; }
}
with 表達式

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

var foo = new
{
  Name = "Foo",
  Email = "[email protected]"
};
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/…的規模上,我認為這將為他們節省一些空間。如果有人仍想在一個文件中包含多個命名空間,則仍然可以選擇使用大括弧。

// 傳統方式 LegacyNamespace.cs
namespace LegacyNamespace
{
  class Foo
  {
    // ToDo 業務邏輯
  }
}

// 簡化後的方式 SimplifiedNamespace.cs
namespace SimplifiedNamespace;
class Bar
{
  // ToDo 業務邏輯
}
lambda 表達式

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

var f = Console.WriteLine;
var f = x => x; // 推斷返回類型
var f = (string x) => x; // 推斷簽名
var f = [NotNull] x => x; // 在屬性上添加特性
var f = [NotNull] (int x) => x;
var f = [return: NotNull] static x => x; // 為返回類型添加特性
var f = T () => default; // 顯示返回類型
var f = ref int (ref int x) => ref x; // 在 struct 上使用 ref 關鍵字
var f = int (x) => x; // 顯式指定隱式輸入的返回類型
var f = static void (_) => Console.Write("Help");
interface介面

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

interface IFoo
{
  static IFoo Empty { get; }
  static operator +(IFoo first, IFoo second);
}
class Foo : IFoo { public static IFoo Empty => new Foo(); public static operator +(IFoo first, IFoo second) => /* 在此做邏輯計算 */; }

 

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

 


參考文獻:

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