C# 10 新特性 —— 补充篇
C# 10 新特性 —— 補充篇
Intro
前面已經寫了幾篇文章介紹 C# 10 新特性的文章,還有一些小的更新
Constant interpolated strings
在之前的版本中,如果想要使用插值字符串來,則不能聲明為一個常量
如果依賴于一個常量的插值字符串就只能聲明為一個 static 的變量,比如說下面這個例子:
private?const?string?Name?=?"Alice"; private?static?readonly?string?Hello?=?$"Hello?{Name}";在 C# 10 中我們可以將 Hello 也聲明為常量(const),如下:
public?string?const?Name?=?"Alice"; public?string?const?Hello?=?$"Hello?{Name}";這種常量插值字符串參數只適用于字符串,如果是 int 就不行了,比如說,如果寫一段下面這樣的代碼:
private?const?int?Num?=?10; private?const?string?HelloNum?=?$"Hello?{Num}";這里插值字符串的參數是一個 int 類型,實際編譯器會直接報錯,報錯信息如下:
// Error ? CS0133 ?The expression being assigned to 'ConstantInterpolatedStringSample.HelloNum' must be constant
上一篇文章中,我們有提到像 int/DateTime 等這種數據字符串插值是會依賴當前的一個 CultureInfo,所以作為插值字符串的時候不是一個編譯時就確定的一個值,不能被認為是常量
這個特性的插值參數必須是字符串常量。
Extended property patterns
C# 10 針對模式匹配有一點小優化,針對嵌套的屬性模式寫法做了一些簡化,如:
if?(e?is?MethodCallExpression?{?Method:?{?Name:?"MethodName"?}?})C# 10 新寫法:
if?(e?is?MethodCallExpression?{?Method.Name:?"MethodName"?})一個完整的簡單小例子如下:
record?TestModelA(TestModelB?B,?string?Name); record?TestModelB(TestModelC?C,?string?Name); record?TestModelC(string?Name);var?a?=?new?TestModelA(new?TestModelB(new?TestModelC("C"),?"B"),?"A"); if?(a?is?{?B.C.Name.Length:?>?0?}) {Console.WriteLine(a.B.C.Name); }Record types can seal ToString
在 C# 10 中我們可以把 record 類型的 ToString() 方法標記為 sealed,這樣繼承于它的子類就不能再重寫這個方法了,可以用來保護 ToString() 輸出的格式,保證輸出的格式是一致的,使用起也很簡單,直接在 ToString 方法中聲明 sealed 即可,示例如下:
record?Person(string?Name,?int?Age) {public?sealed?override?string?ToString(){return?Name;} }這樣如果繼承于它的 record 想要重寫 ToString 方法的時候就會報錯
前面我們提到過 C# 10 中增加了 record struct,它可以使用這個特性嗎?答案是不可以,這一特性僅針對于 record class,在 record struct 中使用會得到一個類似下面的錯誤
Assignment and declaration in same deconstruction
在之前的版本中,我們如果想要使用 tuple 返回值,必須要同時初始化,如下:
private?static?(int?month,?int?day)?GetDate() {var?today?=?DateTime.Today;return?(today.Month,?today.Day); }(var?month,?var?day)?=?GetDate(); Console.WriteLine($"Month:?{month},?day:?{day}");我們必須要同時聲明 month/day,在 C# 10 中我們既可以使用已有變量又可以聲明新的變量,可以結合在一起使用,如下:
(var?month,?var?day)?=?GetDate(); Console.WriteLine($"Month:?{month},?day:?{day}");(month,?var?day2)?=?GetDate(); Console.WriteLine($"Month:?{month},?day:?{day2}");(month,?day)?=?GetDate(); Console.WriteLine($"Month:?{month},?day:?{day}");Improved definite assignment
C# 10 中編譯器會做更多的推斷從而大大方便我們的使用,之前介紹的 Lamdba 的一些優化都是由編譯器做的推斷,針對于初始化賦值的 null 檢查也有一些優化,下面是微軟給出的一個示例,在之前的版本中會有警告,但是在 C# 10 之后就沒有警告了
string?representation?=?"N/A"; if?((c?!=?null?&&?c.GetDependentValue(out?object?obj))?==?true) {representation?=?obj.ToString();?//?undesired?error }//?Or,?using??. if?(c?.GetDependentValue(out?object?obj)?==?true) {representation?=?obj.ToString();?//?undesired?error }//?Or,?using??? if?(c?.GetDependentValue(out?object?obj)????false) {representation?=?obj.ToString();?//?undesired?error }Allow AsyncMethodBuilder attribute on methods
從 C# 10 開始我們可以在異步方法上設置 AsyncMethodBuild 來自定義要處理異步任務的方式,這有對于要實現性能更好的異步方法處理方式的用戶會更加的方便
比如這樣一個異步方法:
public?async?ValueTask<T>?ExampleAsync()?{?...?}實際編譯器會生成這樣的代碼:
[AsyncStateMachine(typeof(<ExampleAsync>d__29))] [CompilerGenerated] static?ValueTask<int>?ExampleAsync() {<ExampleAsync>d__29?stateMachine;stateMachine.<>t__builder?=?AsyncValueTaskMethodBuilder<int>.Create();stateMachine.<>1__state?=?-1;stateMachine.<>t__builder.Start(ref?stateMachine);return?stateMachine.<>t__builder.Task; }使用這種方式,我們可以控制異步方法的處理
[AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]?//?new?usage,?referring?to?some?custom?builder?type static?async?ValueTask<int>?ExampleAsync()?{?...?}這樣實際生成的代碼類似下面這樣
[AsyncStateMachine(typeof(<ExampleAsync>d__29))] [CompilerGenerated] [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]?//?retained?but?not?necessary?anymore static?ValueTask<int>?ExampleAsync() {<ExampleAsync>d__29?stateMachine;stateMachine.<>t__builder?=?PoolingAsyncValueTaskMethodBuilder<int>.Create();?//?<>t__builder?now?a?different?typestateMachine.<>1__state?=?-1;stateMachine.<>t__builder.Start(ref?stateMachine);return?stateMachine.<>t__builder.Task; }更多細節可以參考:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/async-method-builders
Generic Attribute
Generic Attribute 目前還不算是 C# 10 新特性中的一部分,但是已經可以使用了,需要聲明 LangVersion 為 preview,否則會看到類似下面的一個錯誤
Generic Attribute Requires preview LangVersion因為可以用了,而且想在我的項目里使用,所以想嘗試一下,但是就目前來說,還不能滿足我的需要,簡單看一下好了
我們可以這樣用
[ExcelConfiguration<TestModel>] public?class?TestModel {public?int?Id?{?get;?set;?}public?string?Title?{?get;?set;?}?=?string.Empty; }[AttributeUsage(AttributeTargets.Class?|?AttributeTargets.Struct)] public?class?ExcelConfigurationAttribute<T>?:?Attribute {public?string?DefaultFileName?{?get;?set;?}?=?"unnamed-file.xlsx";public?Func<T,?bool>?DataValiadator?{?get;?set;?}?=?_?=>?true; }但是如果想在聲明 Attribute 的地方指定泛型委托會報錯,錯誤信息如下:
'DataValiadator' is not a valid named attribute argument because it is not a valid attribute parameter type
Generic Lambda Error類似的還有一個 Generic Math 的功能也可以預覽使用,但是對我來說感覺意義不是特別大,所以就不介紹了,感興趣的可以參考:https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/
對于預覽版特性,如果不是特別需要建議還是不要輕易在生產代碼里用,以后被砍了就尷尬了
More
原來有很多原本計劃在 C#10 的特性被推到了 C# 11中,比如 filed 關鍵詞、required 成員針對集合的模式匹配等,但總體來說還是有很多不錯的新特性了,還沒用 C# 10 的小伙伴們可以用起來了~~
C# 10 新特性解析的系列文章到此就結束了,如果有錯誤的地方歡迎指出,萬分感謝
C# 10 新特性的示例代碼可以從 Github 上獲取:https://github.com/WeihanLi/SamplesInPractice/tree/master/CSharp10Sample
更多特性介紹可以參考微軟的文檔,可以參考文末的參考鏈接
References
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/constant_interpolated_strings
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/extended-property-patterns
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/improved-definite-assignment
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/async-method-builders
總結
以上是生活随笔為你收集整理的C# 10 新特性 —— 补充篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF 实现温度计
- 下一篇: 值得永久收藏的 C# 设计模式套路(三)