dotnet 警惕 C# 的 is var 写法
本文将和大家介绍 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.md 和 https://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
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 写法的更多相关文章
- 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 ...
- EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)
前言 终于踏出第一步探索EF Core原理和本质,过程虽然比较漫长且枯燥乏味还得反复论证,其中滋味自知,EF Core的强大想必不用我再过多废话,有时候我们是否思考过背后到底做了些什么,到底怎么实现的 ...
- WPF 使用 Silk.NET 进行 DirectX 渲染入门
本文告诉大家如何使用 dotnet 基金会新开源的 Silk.NET 库调用 DirectX 进行渲染的方法.此库是对 DirectX 的底层基础封装,用上了 dotnet 和 C# 的各个新特性,相 ...
- 前端编码规范之JavaScript
上次浅谈了下关于CSS的编码规范,大部分童鞋持赞同意见,仍存在一些童鞋不太理解这些规范的意义. 如果是个人或者小作坊开发,其实这些所谓的编码规范也没啥意思,因为大家写好的代码直接就给扔到网上去了,很少 ...
- 今天第一次接触到typescript,看了第一个知识点就是变量的声明,来回忆回忆,做做笔记
以前只用过JavaScript原生写网站特效,今天还是第一次听说typescript的,然后看了一下它的基本知识,感觉很像Java,真的太像了,但是又有不同点.很让我惊奇看到的第一个知识点就和以前不同 ...
- ES6特性
一.ES6特性: let, const, class, extends, super, arrow functions, template string, destructuring, default ...
- ASP.NET Core 网站发布到Linux服务器
长期以来,使用.NET开发的应用只能运行在Windows平台上面,而目前国内蓬勃发展的互联网公司由于成本的考虑,大量使用免费的Linux平台,这就使得.NET空有一身绝技但无法得到广大的施展空间,.N ...
- ASP.NET Core 网站发布到Linux服务器(转)
出处;ASP.NET Core 网站发布到Linux服务器 长期以来,使用.NET开发的应用只能运行在Windows平台上面,而目前国内蓬勃发展的互联网公司由于成本的考虑,大量使用免费的Linux平台 ...
- NET Core 跨平台执行命令、脚本
一.前言 我们可能会遇到需要在程序中执行一些系统命令,来获取一些信息:或者调用shell脚本..NET Core 目前已经可以跨平台执行,那么它如何跨平台执行命令呢,请看下面的讲解. 二.Proces ...
- FreeSql 过滤器使用介绍
FreeSql.Repository 实现了过滤器,它不仅是查询时过滤,连删除/修改/插入时都会进行验证,避免数据安全问题. 过滤器 目前过滤器依附在仓储层实现,每个仓储实例都有 IDataFilte ...
随机推荐
- 走进volatile的世界,探索它与可见性,有序性,原子性之间的爱恨情仇!
写在开头 在之前的几篇博文中,我们都提到了 volatile 关键字,这个单词中文释义为:不稳定的,易挥发的,在Java中代表变量修饰符,用来修饰会被不同线程访问和修改的变量,对于方法,代码块,方法参 ...
- SnapHelper源码深度解析
目录介绍 01.SnapHelper简单介绍 1.1 SnapHelper作用 1.2 SnapHelper类分析 1.3 LinearSnapHelper类分析 1.4 PagerSnapHelpe ...
- 记录--微信小程序跳转H5、小程序、App
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 在业务中接触了微信小程序,客户对引流用户非常在意,每次都会提该需求,经常做就存档一下.使用的小程序账户都是企业版非个人版本. 跳转H5 在 ...
- 用了两周开源堡垒机OneTerm,我有一些建议
上一篇文章分享了一款简洁且强大的开源堡垒机OneTerm,功能完善,代码简单,GO语言开发,用来学习很合适,拿来自用也没问题.堡垒机该有的核心功能基本都有了,方便与自有系统集成,我使用了两周,功能上没 ...
- Java内存马2-Spring内存马
Spring内存马 目录 Spring内存马 1.Spring&Spring MVC简介 2.环境搭建 3.Controller内存马 4.踩坑日记 5.Interceptor内存马 1.Sp ...
- TCL之基本语法1
TCL之基本语法1 1:set and puts set就是变量声明,这个声明在需要使用的指令下会自动执行,不会出现没有声明的错误.但是对于puts这类定向访问的是不能没有声明的.这里简单理解TCL ...
- Mac 使用 Nginx 在本地部署静态网站
安装 安装 Brew /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/i ...
- Python基础之:Python中的模块
目录 简介 模块基础 执行模块 模块搜索路径 dir 包 包的相对路径 简介 Python的解释环境是很好用,但是如果我们需要编写一个大型的程序的时候,解释环境就完全不够用了.这个时候我们需要将pyt ...
- Go 语言中切片的使用和理解
切片与数组类似,但更强大和灵活.与数组一样,切片也用于在单个变量中存储相同类型的多个值.然而,与数组不同的是,切片的长度可以根据需要增长和缩小.在 Go 中,有几种创建切片的方法: 使用[]datat ...
- mybatis复习(二)
简介 mybatis是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql语句本身, 而不需要花费精力去处理加载驱动.创建连接.创建 statement 等繁杂的 ...