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: MainTest

66d0b12d355ee08fef94bbd28b309ca2.png

CallerArgumentExpression

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 是封装的一个方法,如果有异常会记录一个日志

上面代码执行结果如下:

894cdf74ba15e51ba61739b620173ba6.png

可以看到我们的名称也是被记录了下来 Parameter 名字就是我们传入的变量名,不需要我们再手动的额外加一个 nameof(name)

再来看一个调用示例,调用代码如下:

var num = 10;
InvokeHelper.TryInvoke(() => Verify.InRange(num, 2, 5));
InvokeHelper.TryInvoke(() => Verify.InRange(num, 10 + 2, 10 + 5));

输出结果如下:

ba4e3d4f070ad291b61657a90617f95b.png

如果没有变量名或者属性名等,就会直接用传入进来的 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));

输出结果如下:

dd9b6832eb081fbc35a2f5b53dbf37c7.png

从上面的结果我们可以看到,参数名已经自动的解析出来了

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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,我没有找到关于VS2022中C#新特性的准确信息。然而,根据引用[1提到的C# 7.0新特性,你可能对以下几点感兴趣: 1. 引用提到了命名空间(namespace)的用法。在C#中,命名空间用于组织和管理代码,以避免命名冲突。你可以使用关键字`namespace`来定义命名空间,将代码放置在不同的命名空间中。 2. 引用中提到了C#中的partial类的使用。partial类允许将一个类的定义分成多个部分,在不同的文件中进行定义。这个特性可用于实现UI与逻辑的分离,将绘制UI的代码和类的逻辑代码拆分开来。 请注意,以上提到的是C# 7.0的特性,如果你对VS2022中C#新特性感兴趣,我建议你查阅官方文档或其他可靠来源以获取更准确和详细的信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [干货来袭! C# 7.0 新特性(VS2017可用)](https://download.csdn.net/download/weixin_38653155/12976737)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [VS2022 C# 新语法 using namespace](https://blog.csdn.net/q8812345qaz/article/details/127080222)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [C#入门级-使用VS2022编写C#(包括WPF,XAML基础、创建新项目)](https://blog.csdn.net/dlwlrma_516/article/details/127159830)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值