C# 10 新特性 —— CallerArgumentExpression
C# 10 新特性 —— CallerArgumentExpression
Intro
C# 10 支持使用 CallerArgumentExpression 來自動地獲取調用方的信息,這可以簡化我們現在的一些代碼,讓代碼更加簡潔,一起看下面的示例吧
Caller Info
C# 在 5.0 的時候開始支持 Caller Info 自動獲取調用方的一些信息,C# 5 開始支持的 Caller Info Attribute 有三個,
[CallerMemberName] - 調用方成員名稱,方法名或者屬性名.
[CallerFilePath] - 調用方源代碼所在文件路徑
[CallerLineNumber] - 調用方所在源代碼的行數信息
在方法參數中添加一個 Attribute 來獲取調用方信息,使用示例如下:
public?static?void?MainTest() {//?我是誰,我在哪兒DumpCallerInfo(); }private?static?void?DumpCallerInfo([CallerFilePath]?string??callerFilePath?=?null,[CallerLineNumber]?int??callerLineNumber?=?null,[CallerMemberName]?string??callerMemberName?=?null ) {Console.WriteLine("Caller?info:");Console.WriteLine($@"CallerFilePath:?{callerFilePath} CallerLineNumber:?{callerLineNumber} CallerMemberName:?{callerMemberName} "); }針對 CallerLineNumber 的類型是 int,所以參數類型需要能夠直接接收 int,如上面的 [CallerLineNumber] int? callerLineNumber = null 也可以改成 [CallerLineNumber] int callerLineNumber = -1 或者 [CallerLineNumber] long callerLineNumber = -1
但是不能改成 [CallerLineNumber] string callerLineNumber = null 或者 [CallerLineNumber] short callerLineNumber = -1
string 類型不兼容,short 不能隱式轉換
上面代碼輸出結果類似下面:
Caller?info: CallerFilePath:?C:\projects\sources\SamplesInPractice\CSharp10Sample\CallerInfo.cs CallerLineNumber:?8 CallerMemberName:?MainTestCallerArgumentExpression
CallerArgumentExpression 也是屬于一種 Caller Info
下面這里是利用 CallerArgumentExpression 實現的幾個驗證方法,如果參數不合法就拋出一個異常,通過 CallerArgumenExpression 來自動的獲取調用方的參數表達式
public?static?class?Verify {public?static?void?Argument(bool?condition,?string?message,?[CallerArgumentExpression("condition")]?string??conditionExpression?=?null){if?(!condition)?throw?new?ArgumentException(message:?message,?paramName:?conditionExpression);}public?static?void?NotNullOrEmpty(string?argument,?[CallerArgumentExpression("argument")]?string??argumentExpression?=?null){if?(string.IsNullOrEmpty(argument)){throw?new?ArgumentException("Can?not?be?null?or?empty",?argumentExpression);}}public?static?void?InRange(int?argument,?int?low,?int?high,[CallerArgumentExpression("argument")]?string??argumentExpression?=?null,[CallerArgumentExpression("low")]?string??lowExpression?=?null,[CallerArgumentExpression("high")]?string??highExpression?=?null){if?(argument?<?low){throw?new?ArgumentOutOfRangeException(paramName:?argumentExpression,message:?$"{argumentExpression}?({argument})?cannot?be?less?than?{lowExpression}?({low}).");}if?(argument?>?high){throw?new?ArgumentOutOfRangeException(paramName:?argumentExpression,message:?$"{argumentExpression}?({argument})?cannot?be?greater?than?{highExpression}?({high}).");}}public?static?void?NotNull<T>(T??argument,?[CallerArgumentExpression("argument")]?string??argumentExpression?=?null)where?T?:?class{ArgumentNullException.ThrowIfNull(argument,?argumentExpression);} }來看一個使用調用示例:
var?name?=?string.Empty; InvokeHelper.TryInvoke(()?=>?Verify.NotNullOrEmpty(name));上面的 InvokeHelper.TryInvoke 是封裝的一個方法,如果有異常會記錄一個日志
上面代碼執行結果如下:
可以看到我們的名稱也是被記錄了下來 Parameter 名字就是我們傳入的變量名,不需要我們再手動的額外加一個 nameof(name) 了
再來看一個調用示例,調用代碼如下:
var?num?=?10; InvokeHelper.TryInvoke(()?=>?Verify.InRange(num,?2,?5)); InvokeHelper.TryInvoke(()?=>?Verify.InRange(num,?10?+?2,?10?+?5));輸出結果如下:
如果沒有變量名或者屬性名等,就會直接用傳入進來的 value 字面量,如果傳入進來的是一個表達式,那么記錄下來的就是表達式本身,比如上面輸出的 5/10 + 2,而 num 是傳入的一個變量,就會獲取到變量的名字,是不是很神奇,很多驗證的地方就可以簡化很多了
Sample
CallerArgumentExpression 有一個很典型的一個實際應用就是 .NET 6 里新增的一個 API
ArgumentNullException.ThrowIfNull() 方法,這個方法的實現如下:
///?<summary>Throws?an?<see?cref="ArgumentNullException"/>?if?<paramref?name="argument"/>?is?null.</summary> ///?<param?name="argument">The?reference?type?argument?to?validate?as?non-null.</param> ///?<param?name="paramName">The?name?of?the?parameter?with?which?<paramref?name="argument"/>?corresponds.</param> public?static?void?ThrowIfNull([NotNull]?object??argument,?[CallerArgumentExpression("argument")]?string??paramName?=?null) {if?(argument?is?null){Throw(paramName);} }[DoesNotReturn] private?static?void?Throw(string??paramName)?=>throw?new?ArgumentNullException(paramName);源碼可以從 Github 上看 https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs
我們實際調用的時候就可以不傳參數名,會自動的獲取參數名,示例如下:
object??xiaoMing?=?null; InvokeHelper.TryInvoke(()?=>?ArgumentNullException.ThrowIfNull(xiaoMing));輸出結果如下:
從上面的結果我們可以看到,參數名已經自動的解析出來了
More
升級 .NET 6 的小伙伴快用這個改造你的代碼吧,然后就是很多 null 檢查也可以使用新的 ArgumentNullException.ThrowIfNull 去簡化代碼吧~~
想使用上述代碼測試,可以從 Github 獲取 https://github.com/WeihanLi/SamplesInPractice/blob/master/CSharp10Sample/CallerInfo.cs
References
https://www.codeproject.com/Tips/606379/Caller-Info-Attributes-in-Csharp-5-0
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/caller-argument-expression?WT.mc_id=DT-MVP-5004222
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/caller-information?WT.mc_id=DT-MVP-5004222
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10?WT.mc_id=DT-MVP-5004222#callerargumentexpression-attribute-diagnostics
https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs
https://github.com/WeihanLi/SamplesInPractice/blob/master/CSharp10Sample/CallerInfo.cs
總結
以上是生活随笔為你收集整理的C# 10 新特性 —— CallerArgumentExpression的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 10 新特性 —— 命名空间的变化
- 下一篇: C# 10 新特性 —— Lambda