本文将和大家介绍 C# 语言设计里面,我认为比较坑的一个语法。通过 is var 的写法,会让开发者误以为 null 是不被包含的,然而事实是在这里的 var 是被赋予含义的,将被允许 null 通过判断逻辑,于是就会让开发者收到了奇怪的空异常

比如看看以下的代码,大家猜猜控制台是否会输出

IFoo? foo = null;

if (foo is var f2)
{
Console.WriteLine($"居然进来了。 F2={f2}");
}

答案是控制台居然输出居然进来了,也就是说 null 在 is 判断里面是通过,而 var 的含义似乎不只是一个语法上的可有可无的关键词而已,而是赋予了运行时含义的关键词

换句话说就是在以上代码里面的 var 关键词已经违背了 C# 初始设计 var 里面的含义了。最初的 C# 里面的 var 只是一个在构建过程中可以被平替为具体类型的关键词,是一个不会影响到语义、运行时逻辑的语法而已。然而在 is 这里面,将 var 当成了一个可以处理空值的特殊语法结构

这和咱长久的使用 is 来过滤空值的编程思想是冲突的,我感觉绝大部分开发者在写到 is var 的过程,将会想着应该是自动过滤掉 null 值。然而事实是按照 C# 的新设计(C# 7.0-8.0)来说,这里的 var 是一个模式匹配的语法而已,且 var 不再只是一个可有可无的关键词,而是将会影响运行逻辑的关键词

相信许多开发者会和我一样,第一次编写 is var 的时候,会认为一定会过滤掉空值,导致出现了预期之外的空异常

通过以上的代码测试,可以看到以上代码里面的 var 和 IFoo 是不等价的。咱更进一步编写更多的代码,用来测试一下具体的语法行为,如以下代码的两个 var 的含义是完全不同的

IFoo? foo = null;

var f1 = foo;

if (foo is var f2)
{
Console.WriteLine($"居然进来了。 F2={f2}");
}

第一个 var 是传统的用法,只是让开发者省略编写重复的代码,没有影响到任何的语义和运行逻辑。第一个 var 和 IFoo 是等价的

然而第二个 var 在上面代码里面,却不能够平替为 IFoo 类型,试试看替换为 IFoo 类型试试,如以下代码,大家可以看到运行逻辑是完全不相同的

var f1 = foo;

if (foo is var f2)
{
Console.WriteLine($"居然进来了。 F2={f2}");
} if (foo is IFoo f3)
{
Console.WriteLine($"不进来");
}

如果将 is var 替换为 is IFoo 则非常符合预期的过滤掉 null 值

这个如此奇怪的行为是如何被设计出来的,设计这样的行为为什么能够通过大家的语法评审?难道有这么多的开发者大佬脑袋都被大门夹了?

整个 C# 语言的设计是在不断迭代的,现在已经是 C# 12 了。在当年 C# 7.0 时候引入了 pattern 写法时,大家都为此开森,因为这个语法写起来特别漂亮。然而潜藏的 is var 就在 8.0 的对 pattern 模式匹配里面的更进一步改进里面,不得不被引入了这个奇怪的行为,看看以下咱平时写的很爽的语法

static Point Transform(Point point) => point switch
{
var (x, y) when x < y => new Point(-x, y),
var (x, y) when x > y => new Point(x, -y),
var (x, y) => new Point(x, y),
};

以上的模式匹配里面其实就隐含了 is var 的定义设计,准确来说 is 和 switch 都属于 C# 语法里面的模式匹配的语法,两者应该都有相同的设计

更何况在过滤空对象时,还可以使用 is {} 语法,这就导致了如果将 is var 设计为过滤 null 对象,将会和 is {} 语法是重叠的,浪费关键词。为了能够更好的实现比较长的链路短写法,于是就如官方文档所述将 var 匹配当成为对一切的匹配,包含 null 对象的匹配

换句话说使用 var 匹配就相当于只是拿出来一个变量而已,而不会做其他任何的处理逻辑。用途之处在于大概如下的代码里面

    static bool IsFoo() =>
GetFxx() is var fxx
&& CheckXx(fxx) is var result
&& DoXxx(result);

以上代码可以非常方便的利用短路逻辑和 is var 逻辑取出变量执行后续过程。如此写法的完全展开形式也是非常长的

    static bool IsFoo()
{
if (GetFxx() is var fxx)
{
if (CheckXx(fxx) is var result)
{
return DoXxx(result);
}
} return false;
}

如此可以看来 is var 的设计还是在一些逻辑上可以很好的减少代码量的

这个 is var 的决议最早的有记录的会议可以追溯到 2015 那会,详细请看 https://github.com/dotnet/csharplang/blob/20dde78e36028ac0492035f51e28437a92d1b4f2/meetings/2015/LDM-2015-01-21.mdhttps://github.com/dotnet/csharplang/blob/20dde78e36028ac0492035f51e28437a92d1b4f2/meetings/2015/LDM-2015-03-10-17.md 等会议记录内容

从 IL 层面上看 is var 的语法,可以发现 is var 只是就是一个局部变量赋值,从 IL 上看的 is 判断只是空气而已,什么都没有

如以下的 C# 代码和 IL 的对应,可以看到 if (foo is var f2)var f2 = foo; 是等价的

C#:
if (foo is var f2) IL:
IL_0005: ldloc.0 // foo
IL_0006: stloc.2 // f2 -------------------------------------
C#:
var f2 = foo; IL:
IL_0007: ldloc.0 // foo
IL_0008: stloc.1 // f2

这和 if (foo is IFoo f3) 的逻辑是完全不一样的,如以下的 C# 和 IL 对应代码

C#:
if (foo is IFoo f3) IL:
IL_0007: ldloc.0 // foo
IL_0038: isinst IFoo
IL_003d: stloc.1 // f3
IL_003e: ldloc.1 // f3
IL_003f: brfalse.s IL_006a

本文以上代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 2ec91207fff919837fff1c3121d57d0172b4f2bb

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 2ec91207fff919837fff1c3121d57d0172b4f2bb

获取代码之后,进入 FaydeenereqelnairderlaHuwicagall 文件夹

dotnet 警惕 C# 的 is var 写法的更多相关文章

  1. Cookies 初识 Dotnetspider EF 6.x、EF Core实现dynamic动态查询和EF Core注入多个上下文实例池你知道有什么问题? EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    Cookies   1.创建HttpCookies Cookie=new HttpCookies("CookieName");2.添加内容Cookie.Values.Add(&qu ...

  2. EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    前言 终于踏出第一步探索EF Core原理和本质,过程虽然比较漫长且枯燥乏味还得反复论证,其中滋味自知,EF Core的强大想必不用我再过多废话,有时候我们是否思考过背后到底做了些什么,到底怎么实现的 ...

  3. WPF 使用 Silk.NET 进行 DirectX 渲染入门

    本文告诉大家如何使用 dotnet 基金会新开源的 Silk.NET 库调用 DirectX 进行渲染的方法.此库是对 DirectX 的底层基础封装,用上了 dotnet 和 C# 的各个新特性,相 ...

  4. 前端编码规范之JavaScript

    上次浅谈了下关于CSS的编码规范,大部分童鞋持赞同意见,仍存在一些童鞋不太理解这些规范的意义. 如果是个人或者小作坊开发,其实这些所谓的编码规范也没啥意思,因为大家写好的代码直接就给扔到网上去了,很少 ...

  5. 今天第一次接触到typescript,看了第一个知识点就是变量的声明,来回忆回忆,做做笔记

    以前只用过JavaScript原生写网站特效,今天还是第一次听说typescript的,然后看了一下它的基本知识,感觉很像Java,真的太像了,但是又有不同点.很让我惊奇看到的第一个知识点就和以前不同 ...

  6. ES6特性

    一.ES6特性: let, const, class, extends, super, arrow functions, template string, destructuring, default ...

  7. ASP.NET Core 网站发布到Linux服务器

    长期以来,使用.NET开发的应用只能运行在Windows平台上面,而目前国内蓬勃发展的互联网公司由于成本的考虑,大量使用免费的Linux平台,这就使得.NET空有一身绝技但无法得到广大的施展空间,.N ...

  8. ASP.NET Core 网站发布到Linux服务器(转)

    出处;ASP.NET Core 网站发布到Linux服务器 长期以来,使用.NET开发的应用只能运行在Windows平台上面,而目前国内蓬勃发展的互联网公司由于成本的考虑,大量使用免费的Linux平台 ...

  9. NET Core 跨平台执行命令、脚本

    一.前言 我们可能会遇到需要在程序中执行一些系统命令,来获取一些信息:或者调用shell脚本..NET Core 目前已经可以跨平台执行,那么它如何跨平台执行命令呢,请看下面的讲解. 二.Proces ...

  10. FreeSql 过滤器使用介绍

    FreeSql.Repository 实现了过滤器,它不仅是查询时过滤,连删除/修改/插入时都会进行验证,避免数据安全问题. 过滤器 目前过滤器依附在仓储层实现,每个仓储实例都有 IDataFilte ...

随机推荐

  1. python基础五(文件操作)

    一 文件操作 一 介绍 计算机系统分为:计算机硬件,操作系统,应用程序三部分. 我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,众 ...

  2. iis管理器界面打不开

    iis管理器界面打不开 图形界面打不开 服务正常运行 开始->运行->输入以下重置下 inetmgr.exe /reset

  3. 「AntV」全球AQI数据获取与L7可视化

    1. 引言 L7 地理空间数据可视分析引擎是一种基于 WebGL 技术的地理空间数据可视化引擎,可以用于实现各种地理空间数据可视化应用.L7 引擎支持多种数据源和数据格式,包括 GeoJSON.CSV ...

  4. 给你的wordpress添加文章内图片鼠标点击放大浏览的功能吧~

    注:笔者已启用WP Githuber MD插件使用Markdown语法进行文章编辑,启用的主题为generatepress. 1.进入你的宝塔面板首页 点击文件选项: 2.分别找到以下几个文件进行修改 ...

  5. 【已解决】mybatis注解@Param失效,无法获取到值(org.apache.ibatis.binding.BindingException: Parameter 'policy' not found. Available parameters are [arg1, arg0, param1, param2])

    案发现场: 传递的参数是一个实体类 PolicyDictionary 此时我无法拿到数据: 解决思路一(不推荐) 删去@Param注解,使用mybatis默认的参数顺序: 不使用@param注解传递多 ...

  6. Python爬虫爬取国家统计局网站【统计用区划和城乡划分代码】并存入MySQL数据库

    国家统计局网站相关分级页面截图 基本思路 爬取每个页面的a标签内容,生成省市两级数据字典,最后合成区县对应的链接,爬取第三层区划代码和名字,结合省市两级名字生成最后的标准. 代码 1 import p ...

  7. 链表队列(LinkedListQueue)

    栈操作 入队 template<typename T> void LinkedListQueue<T>::enqueue(T e) { if (tail == nullptr) ...

  8. CTFshow pwn49 wp

    PWN49 用ida打开我们发现是静态编译的,所以先要通过libc库来打是不可能的了,程序里面有一个栈溢出点,找一下有没有system函数,发现并没有 那么我们找一下有没有mprotect函数如果有这 ...

  9. #线性筛,哈希#CF1225D Power Products

    题目 给定一个长度为 \(n\) 的正整数序列 \(a\),问有多少对 \((i,j),i<j\) 使得存在一个整数 \(x\) 满足 \(a_i\times a_j=x^k\) 分析 将 \( ...

  10. 响应式系统reactive system初探

    目录 初识响应式系统 什么是响应式系统 响应式系统的四大特点 及时响应性(Responsive) 恢复性(Resilient) 有弹性(Elastic) 消息驱动(Message Driven) 总结 ...