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

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

上面代码执行结果如下:

可以看到我们的名称也是被记录了下来 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相关推荐

  1. C# 10 新特性 —— 补充篇

    C# 10 新特性 -- 补充篇 Intro 前面已经写了几篇文章介绍 C# 10 新特性的文章,还有一些小的更新 Constant interpolated strings 在之前的版本中,如果想要 ...

  2. C# 10 新特性 —— 插值字符串优化

    C# 10 新特性 -- 插值字符串优化 Intro 字符串应该是我们平时使用的最多的一个类型,从 C# 6 开始我们开始支持了插值字符串,使得我们可以更方便的进行字符串的操作,现在很多分析器也推荐我 ...

  3. C# 10 新特性 —— Lambda 优化

    C# 10 新特性 -- Lambda 优化 Intro C# 10 对于 Lambda 做了很多的优化,我们可以在 C# 中更加方便地使用委托和 Lambda 了,下面就来看一些示例 Lambda ...

  4. C# 10 新特性 —— 命名空间的变化

    C# 10 新特性 -- 命名空间的变化 Intro C# 10 针对命名空间做了一些改变,主要是 Global Usings 和 File-scoped Namespace,我们前面分享的示例其实也 ...

  5. JDK 5 ~ 10 新特性倾情整理

    转载自 JDK 5 ~ 10 新特性倾情整理 最近连 JDK11都在准备发布的路上了,大家都整明白了吗?也许现在大部分人还在用6-8,8的新特性都没用熟,9刚出不久,10-11就不用说了. 为了大家对 ...

  6. JavaScript高级之ECMASript 7、8 、9 、10 新特性

    第3章 ECMASript 7 新特性 3.1. Array.prototype.includes Includes 方法用来检测数组中是否包含某个元素,返回布尔类型值 3.2. 指数操作符 在ES7 ...

  7. Java 10新特性

    Java 10新特性 Java 10是其23年历史中最快的java版本.Java因其缓慢的增长和发展而受到批评,但Java 10刚刚破坏了这一概念.Java 10是一个具有许多未来变化的版本,其范围和 ...

  8. Java 10 新特性概述

    Java 10是其23年历史中最快发布的java版本.Java因其缓慢的增长和发展而受到批评,但Java 10刚刚破坏了这个概念.Java 10是一个具有许多未来变化的版本,其范围和影响可能并不明显, ...

  9. Python3.10新特性初体验

    Python3.10新特性初体验 注:图片来源 目录 结构模式匹配 [PEP 635] union类型允许X | Y [PEP 604] 带圆括号的上下文管理器 一.结构模式匹配(新增PEP 635) ...

最新文章

  1. webpack2--webpack 4.X 快速创建demo
  2. JDBC的两种sql命令发送器比较【Statement:PreparedStatement】
  3. linux apache2 伪静态,linux Ubuntu apache2 伪静态设置
  4. How To Make JMeter Behave More Like A Real Browser
  5. VMware VSphere 虚拟化云计算学习配置笔记(四)
  6. 文末送书 | Facebook:易于解释的神经元可能会阻碍神经网络的学习
  7. Xcode插件管理工具Alcatraz
  8. Windows-Server下加强系统安全性系列之方案【六】
  9. 数据科学包15-matplotlib详细介绍
  10. ELK详解(二十一)——elastalert介绍与安装
  11. Android MediaPlayer的生命周期
  12. Meta 开源首个 AI 语音翻译系统,闽南话和英语可以直接语音互译
  13. G1 Concurrent Refinement Thread 在干啥?
  14. linux下PNG、JEPG、JPG、Webp图片格式互转
  15. pycharm 如何配置主题背景色 墨绿色
  16. 网站系统开发公司分析
  17. 11、Nepxion Discovery 之全链路界面操作蓝绿灰度发布
  18. 程序的灵活性与可扩展性
  19. Jitpack使用指南:maven-publish如虎,jitpack如翼 【安卓Java组件化模块化】【更多gradle技巧】
  20. 【BiSeNet】《BiSeNet:Bilateral Segmentation Network for Real-time Semantic Segmentation》

热门文章

  1. Fiddler (五) Mac下使用Fiddler
  2. ISA server的常见身份验证方式
  3. Docker:Nginx-Redis-Mysql-PHP 部署
  4. Oracle数据库查询用 where in 查询的项超过1000条的解决方案
  5. Java快速入门-01-基础篇
  6. php用两个栈来实现队列
  7. 异常处理、socke基于TCP协议编程
  8. QQ,MSN,Skype在线客服代码
  9. 下拉刷新:继承listView控件
  10. 2014-11-25nbsp;11:26