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

張傳寧發表於2021-06-02

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

.NET6系列: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 = "foo@mail.com"
};
var bar = foo with {Name = "Bar"};
namespace 名稱空間

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

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

  隨後,還會對 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#的未來是光明的。

 


參考文獻:

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

 

相關文章