C#10 新特性 [调用方参数表达式] 解决了我七年前的困惑
C#10 新特性 [调用方参数表达式] 解决了我七年前的困惑
目录
一、问题
二、转机
三、实践
1、演示输出各种形式的参数表达式
2、参数不符合条件时抛出异常
3、获取调用扩展方法的表达式
四、结语
独立观察员 2022 年 2 月 13 日
一、问题
时间拉回到 2015 年,那年 3 月,我还没有毕业,不过已经在公司里实习了,从大三暑假开始,到那时候,已经快实习一年了(毕业后才能转正)。对于工作还是比较满意的,九点多上班(看班车什么时候到),十一点可以吃午饭,吃完饭周边散个步,然后回公司午休,下午基本坐 5 点四十 的班车回家,双休;当时组里的小伙伴们气氛也比较好,组长也比较好,我们主要负责公司内部二十多个 OA 系统(全公司一两千人),任务安排得也不是很紧;本来大学学的是 Java,公选课学了 C# 就爱上了,实习用的是现在早已过时的 Webform,当然还有 SQL;实习嘛,经常也是边学边做,经常在网上找解决方案,用麦库记事(已倒闭)做笔记,还有用问答网站进行提问,用得比较多的就是待会儿要出场的 “思否”,偶尔用的还有昙花一现的 “德问”。
当时有一个业务,具体的忘了,只记得用到了反射,当时为了写更少的代码,想要在方法中获取调用者传参时的实参的变量名,不知道怎么弄,于是在 segmentfault.com(思否)网站上提了这么一个问题 ——“C# 如何通过形参获得实参的名字?”(https://segmentfault.com/q/1010000002592470):
经过一番讨论与思考,当时我妥协了,认识到这是不可能实现的:
二、转机
直到昨天看到有人转载了一篇 微软中国 MSDN 的公众号文章《C# 10 的新特性》,在最后部分写了这么一段(灰色的原文链接有误,后面会给出正确的):
当看到下图框出的字符 b 时,我的思绪一下被拉到了七年前,这不就是我当时说服了自己把它当作不可能的事吗,现在竟然变成了可能!只能说,微软 NB!
而且,上图没有框出的另外两个例子还展示了这个功能更强大的地方 —— 不光是形参名称(或者应该叫字面量?),任何表达式它都能原样获取,比如 “true” 和 “a > 5”。
那么这个强大的功能叫什么名字呢?它就是 CallerArgumentExpressionAttribute,可以称之为 “调用方参数表达式特性”。上面公众号文章截图中的参考链接有误,正确的应该是这个 ——https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/attributes/caller-information#argument-expressions :
可以看到它也是在 System.Runtime.CompilerServices 命名空间中,相当于扩充了原先的调用方信息,之前的调用方信息有三项,分别是 文件路径、行号、方法名,现在新增了参数表达式。关于旧的调用方信息三巨头的使用,可以参考我之前的文章《C# 在自定义的控制台输出重定向类中整合调用方信息》。
三、实践
下面开始实践,例子都来源于微软,上面也都提到了。
1、演示输出各种形式的参数表达式
首先就给我来了个下马威,我用 VS2022 打开之前的解决方案总是有各种问题:项目都被卸载了,也重新加载不了;点击重新加载具有依赖项的项目也不行;点击安装缺少的功能,提示已安装,点击启动就会又启动一个 VS2022,打开解决方案还是这样;感觉就是有 Bug。
然后用 VS2019 进行开发,代码都写完了,运行也没有报错,但是没有效果:
当然,这可能不能怪 VS2019,因为公众号文章开头是这样说的:
我们很高兴地宣布 C# 10 作为 .NET 6 和 Visual Studio 2022 的一部分已经发布了。
就是说应该是需要满足 .NET 6 和 VS2022 这两个条件的。然后既然 Visual Studio 2022 不争气,那么我们来试试 Rider:
果然成功了!jetbrains NB!
2、参数不符合条件时抛出异常
帮助方法如下:
using System;
using System.Runtime.CompilerServices;
namespace WPFPractice.Utils
{
public class MethodHelper
{
/// <summary>
/// 验证参数(不符合条件则抛出异常)
/// </summary>
/// <param name="parameterName"> 参数名 </param>
/// <param name="condition"> 条件结果(调用时传条件表达式)</param>
/// <param name="message"> 消息(无需填写,通过调用方参数表达式功能自动获取条件参数的输入表达式)</param>
public static void ValidateArgument(string parameterName, bool condition,
[CallerArgumentExpression("condition")] string? message = null)
{
if (!condition)
{
throw new ArgumentException($"Argument failed validation: <{message}>", parameterName);
}
}
}
}
当然,如果只是为了判断参数是否为 null,且为 null 时抛出异常,微软公众号文章中提到了一个现成的方法:
void MyMethod(object value)
{
ArgumentNullException.ThrowIfNull(value);
}
反编译后可以看到是微软库 Microsoft.NETCore.App6.0.2System.Private.CoreLib.dll 中的功能,也是用到了 “调用方参数表达式”:
3、获取调用扩展方法的表达式
因为扩展方法也可以当做静态方法来使用( var sequence = ExtensionTester.GetNewSequence (Enumerable.Range (0, 10), 100); ),所以也就很好理解了。另外,微软的例子中没有我后面加的那句 ToList () 的操作,我试了是不行的,因为 Linq 的延迟执行的特性(要实际用到才会执行),如果没有那句,本例的扩展方法不会被执行。
四、结语
就像开头讲述的那样,实际上我昨天看到这个功能时还是挺激动的,虽然只是个不起眼的小功能,但是那种感觉就像是:一件尘封多年的悬案,因为时代的局限,基本被视作无法找到真相了,突然有一天,由于科技的进步(比如指纹技术或者 DNA 检测技术),终于真相大白。
好了,有点晚了,本文明天再发布,明天是情人节,祝我好运吧,也不知道我这个人生的 “悬案” 什么时候能告破。
最后给出源码地址:https://gitee.com/dlgcy/DLGCY_WPFPractice/tree/Blog20220213
感谢阅读。
相关文章
- ADO数据库C#中ExecuteReader、ExecuteNonQuery、ExecuteScalar、SqlDataReader、SqlDataAdapter
- 完美解决C#中拖动splitContainer分割线时显示虚线问题
- c#实战教程_ps初学者入门视频
- 通过Queue 类解决C#数据并发问题
- c# 多线程并发-金三银四面试:C#.NET面试题高级篇2-多线程
- c#面试题抽象类和接口的区别-金三银四面试:C#程序员经常遇到的30道基础面试题,想你所想
- c#打包发布时出现错误的解决方法总结详解编程语言
- c#数组详解
- PHP与C#分别格式化文件大小的代码
- c#解决IIS写Excel的权限问题
- C#WinForm捕获全局变量异常SamWang解决方法
- C#HttpClientCookie验证解决方法
- 解析c#在未出现异常情况下查看当前调用堆栈的解决方法
- 解决C#中WebBrowser的DocumentCompleted事件不执行的实现方法
- 解决C#中取消方向键对控件焦点控制的实现方法
- C#实现窗体淡入淡出效果的方法总结
- 使用C#Winform应用程序获取网页源文件的解决方法
- C#删除文件目录或文件的解决方法
- C#实现协同过滤算法的实例代码
- 浅析C#数据类型转换的几种形式
- C#mysql插入数据,中文乱码的解决方法
- c#开发word批量转pdf源码分享
- IE6下javasc#ipt:void(0)无效的解决方法
- c#实现网站监控查看是否正常示例
- c#递归生成XML实例
- C#线程间不能调用剪切板的解决方法
- C#生成条形码图片的简单方法
- C#百万数据查询出现超时问题的解决方法