原文: [Introducing C# 10]

作者: Ken Bonny

本週早些時候(譯註:原文發表於5月1日),我關注了 Mads TorgersenDotNet SouthWest 大會上的演講,他是微軟的 C# 語言的首席設計師。他概述了 C# 10 即將包含的很酷的一些新東西。讓我們來快速瀏覽一下。

小小的免責宣告,這些變化中的大部分已經基本完成。但是由於它仍在積極的開發中,我不能保證 C# 10 釋出時所有東西都會完全如實。

struct record

他談到的第一件事是,目前 record 的實現是使用一個 class 作為基礎物件的。現在還會有一個 record struct 的變體可用,所以基礎型別可以是一個值型別。區別在於,普通的 record 在函式之間傳遞的是引用,而 record struct 是其值的拷貝。 record struct 也會支援 with 運算子。

同時,還允許向 record新增運算子,兩種 record 型別都可以。

record Person(string Name, string Email)
{
public static Person operator +(Person first, Person second)
{
// logic goes here
}
}

required

C# 團隊關注的目標之一是使物件的初始化變得更容易。這就是為什麼可以對 class、struct、record 或 struct record 的屬性新增 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

為了進一步的改善屬性,將允許完全擺脫 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 ,那麼就可以在任何.cs檔案(我建議在 Program.cs 或專門的 Imports.cs )中新增一行 global using Microsoft.Extensions.Logging.ILogger,之後這個名稱空間將可以在整個專案中使用。注意,這不適用於整個解決方案! 沒有人能夠預測哪些地方需要匯入,所以它們被分組到每個專案中。

// 譯註:原文並沒有提供程式碼示例,為了更好方便大家理解私自添加了一個演示
// Program.cs 檔案
global using System; // Sample.cs 檔案
// 可以不用再using System;
Console.WriteLine("foo");

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

// 傳統的方式 LegacyNamespace.cs
namespace LegacyNamespace
{
class Foo
{
// legacy code goes here
}
} // 簡化的方式 SimplifiedNamespace.cs
namespace SimplifiedNamespace;
class Bar
{
// awesome code goes here
}

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; // 使用 ref
var f = int (x) => x; // 顯式指定隱式輸入的返回型別
var f = static void (_) => Console.Write("Help");

感謝 Schooley 提出了一個不那麼容易混淆的特性例子

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) => /* do calculation here */;
}

就個人而言,我喜歡這些變化。我最喜歡的是對名稱空間的改變和對介面的改進。總之,未來是光明的 C# 的。嗯嗯... (譯註:這裡作者玩了一個梗,原文 the future is seeing sharp,see sharp發音類似 C# )

謝謝各位,大家再見。