原文 | Kathleen
翻譯 | 鄭子銘
Visual Studio 17.1(Visual Studio 2022 Update 1)和 .NET SDK 6.0.200 包含 C# 11 的預覽功能!您可以更新 Visual Studio 或下載最新的 .NET SDK 來獲得這些功能。
查看帖子 Visual Studio 2022 17.1 現已推出!了解 Visual Studio 中的新增功能和發布 .NET 7 Preview 1 的帖子以了解更多 .NET 7 預覽功能。
設計 C# 11
我們喜歡公開設計和開發!您可以在 CSharpLang 存儲庫中找到有關未來 C# 功能的建議和語言設計會議的注釋。主頁解釋了我們的設計過程,您可以在 .NET Community Runtime and Languages Standup 上收聽 Mads Torgersen,他在其中談到了設計過程。
一旦計划好某個功能的工作,工作和跟蹤就會轉移到 Roslyn 存儲庫。您可以在功能狀態頁面上找到即將推出的功能的狀態。您可以看到我們正在進行的工作以及合並到每個預覽中的內容。您還可以回顧以前的版本以檢查您可能忽略的功能。
在這篇文章中,我將這些有時是復雜的技術性討論提煉成代碼中每個特性的含義。
我們希望您能試用這些新的預覽功能,並讓我們知道您的想法。要試用 C# 11 預覽功能,請創建一個 C# 項目並將 LangVersion 設置為 Preview。您的 .csproj 文件可能如下所示:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
</Project>
C# 11 預覽:允許在插值字符串的“孔”中換行
在提案中閱讀有關此更改的更多信息,刪除非逐字插值字符串中的插值不能包含換行符的限制。 #4935
C# 支持兩種類型的內插字符串:逐字和非逐字內插字符串(分別為 $@"" 和 $"")。它們之間的一個關鍵區別是非逐字插值字符串不能在其文本段中包含換行符,而必須使用轉義符(如 \r\n)。逐字插值字符串可以在其文本段中包含換行符,並且不會轉義換行符或其他字符(除了“”來轉義引號本身)。所有這些行為保持不變。
以前,這些限制擴展到非逐字插值字符串的孔。孔是表示插值表達式的簡寫方式,是花括號內提供運行時值的部分。孔本身不是文本,不應遵守內插字符串文本段的轉義/換行規則。
例如,以下內容會導致 C# 10 中的編譯器錯誤,並且在此 C# 11 預覽版中是合法的:
var v = $"Count ist: { this.Is.Really.Something()
.That.I.Should(
be + able)[
to.Wrap()] }.";
C# 11 預覽:列表模式
閱讀更多關於提案列表模式中的這種變化。
新的列表模式允許您匹配列表和數組。您可以匹配元素,並且可以選擇包含匹配零個或多個元素的切片模式。使用切片模式,您可以丟棄或捕獲零個或多個元素。
列表模式的語法是方括號括起來的值,切片模式是兩個點。切片模式后面可以跟另一個列表模式,例如 var 模式來捕獲切片的內容。
模式 [1, 2, .., 10] 匹配以下所有內容:
int[] arr1 = { 1, 2, 10 };
int[] arr1 = { 1, 2, 5, 10 };
int[] arr1 = { 1, 2, 5, 6, 7, 8, 9, 10 };
要探索列表模式,請考慮:
public static int CheckSwitch(int[] values)
=> values switch
{
[1, 2, .., 10] => 1,
[1, 2] => 2,
[1, _] => 3,
[1, ..] => 4,
[..] => 50
};
當它傳遞以下數組時,結果如下所示:
WriteLine(CheckSwitch(new[] { 1, 2, 10 })); // prints 1
WriteLine(CheckSwitch(new[] { 1, 2, 7, 3, 3, 10 })); // prints 1
WriteLine(CheckSwitch(new[] { 1, 2 })); // prints 2
WriteLine(CheckSwitch(new[] { 1, 3 })); // prints 3
WriteLine(CheckSwitch(new[] { 1, 3, 5 })); // prints 4
WriteLine(CheckSwitch(new[] { 2, 5, 6, 7 })); // prints 50
您還可以捕獲切片模式的結果:
public static string CaptureSlice(int[] values)
=> values switch
{
[1, .. var middle, _] => $"Middle {String.Join(", ", middle)}",
[.. var all] => $"All {String.Join(", ", all)}"
};
列表模式適用於任何可數和可索引的類型——這意味着它具有可訪問的 Length 或 Count 屬性,並且具有 int 或 System.Index 參數的索引器。切片模式適用於任何可數和可切片的類型——這意味着它具有一個可訪問的索引器,該索引器將 Range 作為參數,或者具有一個具有兩個 int 參數的可訪問的 Slice 方法。
我們正在考慮在 IEnumerable 類型上添加對列表模式的支持。如果您有機會使用此功能,請告訴我們您對此的想法。
C# 11 預覽:參數空值檢查
在提案參數空檢查中閱讀有關此更改的更多信息。
我們將此功能放入此早期預覽版中,以確保我們有時間獲得反饋。已經討論過一種非常簡潔的語法與一種更冗長的語法。我們希望獲得客戶反饋以及有機會嘗試此功能的用戶。
使用樣板代碼的變體來驗證方法參數是否為空是很常見的,例如:
public static void M(string s)
{
if (s is null)
{
throw new ArgumentNullException(nameof(s));
}
// Body of the method
}
使用參數空檢查,您可以通過添加 !! 到參數名稱來縮寫您的意圖:
public static void M(string s!!)
{
// Body of the method
}
將生成代碼以執行空值檢查。生成的空值檢查將在方法中的任何代碼之前執行。對於構造函數,空值檢查發生在字段初始化、調用基構造函數和調用 this 構造函數之前。
此功能獨立於可空引用類型 (NRT),盡管它們可以很好地協同工作。 NRT 可幫助您在設計時了解 null 是否可能。參數空值檢查可以更輕松地在運行時檢查空值是否已傳遞給您的代碼。當您的代碼與可能未啟用 NRT 的外部代碼交互時,這一點尤其重要。
檢查和 if (param is null) throw new ArgumentNullException(...) 是等效的。當多個參數包含 !! 運算符,則檢查將按照聲明參數的順序進行。
下面是一些 !! 在哪里可以使用的限制規則:
- 只有在有實現時才能將空檢查應用於參數。例如,抽象方法參數不能使用 !!。其他不能使用的情況包括:
- 外部方法參數。
- 委托參數。
- 當方法不是默認接口方法 (DIM) 時的接口方法參數。
- 空值檢查只能應用於可以檢查的參數。
根據第二條規則排除的場景示例是丟棄和輸出參數。可以對 ref 和 in 參數進行空值檢查。
允許對索引器參數進行空檢查,並將檢查添加到 get 和 set 訪問器。例如:
public string this[string key!!] { get { ... } set { ... } }
Null-checks 可以用於 lambda 參數,無論它們是否被括號括起來:
// An identity lambda which throws on a null input
Func<string, string> s = x!! => x;
異步方法可以有空檢查參數。調用方法時會發生空值檢查。
該語法對迭代器方法的參數也有效。調用迭代器方法時會發生空值檢查,而不是遍歷底層枚舉器時。這適用於傳統或異步迭代器:
class Iterators {
IEnumerable<char> GetCharacters(string s!!) {
foreach (var c in s) {
yield return c;
}
}
void Use() {
// The invocation of GetCharacters will throw
IEnumerable<char> e = GetCharacters(null);
}
}
與可空引用類型的交互
任何具有 !! 的參數應用於其名稱的運算符將以可空狀態為非空開始。即使參數本身的類型可能為 null,也是如此。這可能發生在顯式可為空的類型(例如字符串?)或不受約束的類型參數中。
當 !!參數上的語法與參數上的顯式可空類型相結合,編譯器將發出警告:
void WarnCase<T>(
string? name!!, // CS8995 Nullable type 'string?' is null-checked and will throw if null.
T value1!! // Okay
)
構造函數
當您從代碼中的顯式空檢查更改為使用空驗證語法 (!!) 進行空檢查時,會有一個很小但可以觀察到的變化。您的顯式驗證發生在使用 this 調用的字段初始值設定項、基類構造函數和構造函數之后。使用參數空檢查語法執行的空檢查將在任何這些執行之前發生。早期的測試人員發現這個順序很有幫助,我們認為這種差異很少會對代碼產生不利影響。但在從顯式空檢查轉移到新語法之前,請檢查它是否不會影響您的程序。
設計注意事項
您可以聽到 Jared Parsons 在 2022 年 2 月 9 日的 .NET 語言和運行時社區站會中的演講。當 Jared 加入我們的行列時,該剪輯開始了大約 45 分鍾,更多地討論了將這個功能引入預覽的決定,並做出了回應一些常見的反饋。
有些人在看到 PR 在 .NET 運行時使用此功能時了解了此功能。 Microsoft 的其他團隊提供了有關 C# 的重要 dogfooding 反饋。得知 .NET 運行時使用這種新的空檢查語法刪除了近 20,000 行代碼,這令人興奮。
在參數名稱上的語法是 !!。它在名稱上,而不是類型上,因為這是在您的代碼中如何處理該特定參數的一個特征。我們決定不使用屬性是因為它會如何影響代碼的可讀性,並且因為屬性很少會像此功能那樣影響程序的執行方式。
我們考慮並拒絕了對所有可空參數進行空檢查的全局設置。參數空值檢查強制設計選擇如何處理空值。有許多方法,其中 null 參數是有效值。在類型不為 null 的任何地方都這樣做會過度,並且會對性能產生影響。僅限制於易受 null 影響的方法(例如公共接口)將是極其困難的。我們還從 .NET 運行時工作中了解到,有很多地方不適合進行檢查,因此需要按參數選擇退出機制。我們目前認為運行時空值檢查的全局方法可能不合適,如果我們考慮使用全局方法,那將是一個不同的特性。
總結
Visual Studio 17.1 和 .NET SDK 6.0.200 提供了對 C# 11 的早期了解。您可以在插值字符串的花括號(孔)內使用參數空檢查、列表模式和新行。
我們希望您通過更新 Visual Studio 或下載最新的 .NET SDK,然后將 LangVersion 設置為預覽來查看 C# 11 預覽功能。
我們期待聽到您的想法,在這里或通過 GitHub 上的 CSharpLang 存儲庫中的討論!
原文鏈接
本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含鏈接: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。
如有任何疑問,請與我聯系 (MingsonZheng@outlook.com) 。