C#8.0可空引用类型的使用注意要点
最近VS2019正式版发布了,装下来顺便试用了一下C#8.0,最大的看点应该就是可空引用类型了。不过C#8.0仍然处于Beta的状态,而且试用时也遇到了几个坑。
背景知识说明:
所谓的可空引用类型是指,一旦启用了可空引用类型这个新特征,引用类型将默认被视为不可空,无法赋予null,除非手工将它设为可空引用类型。
实战示例:
首先是新建一个C#的项目,在 项目文件(.csproj)里加入两行配置,目的是启用“C#8.0语言”和“可空引用类型”:
<LangVersion>8.0</LangVersion>
<NullableContextOptions>enable</NullableContextOptions>
整个文件看起来是这样的:

这样就算是整个项目全局启用了可空引用类型了。
注意:
在VS2019正式版中,使用
<NullableContextOptions>enable</NullableContextOptions>
而不是使用
<NullableReferenceTypes>true</NullableReferenceTypes>
后者在正式版中已经失效了。
如果不希望全局启用可空引用类型的话,可以在程序代码中加入以下编译指令:
#nullable enable
这样可以在加入了该指令的文件中,单独启用可空引用类型。但是,极度不推荐这种做法。为什么呢?因为该指令仅仅在该文件中有效,不能跨文件生效,从而无法阻止null逃逸到使用了该指令的文件中,也就是说,用了也等于没用。
一个很简单的例子足以证明:

注意上面项目文件中并没有全局启用可空引用类型,而下面的Class1.cs中使用了编译器指令来单独启用可空引用类型。


从运行结果可见,空引用仍然逃逸到使用了该指令的作用域中了。别说编译错误,连编译警告都没有。完全达不到理想的效果。

因此,强烈建议在项目文件中全局启用可空引用类型,而不是在某个源文件中单独使用。
另外,还有一点要注意的是,即使启用了可空引用类型后,默认情况下,即使对不可空引用赋予null,编译器也只会生成编译警告,而不是编译错误。仍然是能够编译通过的。一个大项目中,编译警告不可避免,甚至可能会很多,从而淹没了“给不可空引用类型赋予空值”这种不起眼的警告。
因此,建议将特定的警告视为错误。警告编号为8600、8625、8618、8604,或者将所有警告视为错误。具体是在项目文件中加入以下设置(见图一):
<WarningsAsErrors>8600 8625 8618 8604</WarningsAsErrors>
或者在项目编辑器中设置也可以:

这是我自己测试得出的结果,可能还有其它潜在的相关警告编号我没有测试出来。如果有谁知道的话,告诉我一下,谢谢。
做好这些配置之后,可以看到引用类型默认都不能赋予空值了:

这时候普通的引用类型的变量和参数都不能设为null了。
这样可以防止空值扩散开来,引起恼人的空引用异常。
但是,这里有个坑需要注意!!!!
struct里不适用可空引用的规则!!
struct里不适用可空引用的规则!!
struct里不适用可空引用的规则!!

这种情况下直接运行,仍然会抛出空引用异常!!!C#8.0仍未能完全封堵住空引用的逃逸。

其实我还是比较赞同用不可空引用类型的方案的,而不是可空引用类型的方案。毕竟我想要的,只不过是一个不可空的断言,只是想利用不可空引用来划分安全边界,从而防止空值的扩散。简单来说就是想将变量和参数明确声明为不可空引用类型。因为历史和现实的原因,大量的库都还没能全面使用可空引用类型。这种情况下,只有我使用可空引用类型,是不靠谱的。无法划分安全边界。
然而C#选择了可空引用类型的方案,而且还不是强制启用,而且默认只是警告。。跟没有一样。。。
比方说,我使用了一个第三方库项目,而空值的来源是正好是该库项目的,而我对这个库并没有源代码或者修改权限。这时候就无法阻止空值逃逸到我的项目中了。
还是之前的代码,只是稍微做一下修改。新增了一个库项目ClassLibrary1,这个库并没有使用可空引用类型。

库的代码如下:

很简单,就是LibClass3.GetInstance()本应该返回LibClass2的实例,但是出于某种原因,返回了null。但是我的项目中使用了LibClass2和LibClass3。我的项目是全局启用了可空引用类型的:


编译正常,毫无警告和错误。一旦运行,则抛出空引用异常:

可见,目前来说,C#8.0的可空引用类型并不能解决外源性的空值扩散,只能解决内源性的空值扩散,无法跨模块生效。还是洗洗睡吧。
参考资料:
https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/index
https://www.youtube.com/watch?v=VdC0aoa7ung
C#8.0可空引用类型的使用注意要点的更多相关文章
- C#8.0—非空引用类型
非空引用类型--C#8.0 原文地址:https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/?utm_sourc ...
- C#8.0 可空引用类型
介绍 我们的项目代码运行时最频繁的错误之一就是 System.NullReferenceException 异常,c#8.0增加的可为空引用类型就是用来帮助开发者降低甚至消除NULL异常.我们需要注意 ...
- 迫不及待地体验了一把 C#8.0 中的可空引用类型(Nullable Reference)
在我之前的一篇博客 NullReferenceException,就不应该存在! 中,我吐槽了 C# 中 null 的弊端以及避免 null 的方法:事实上这本都是现代高级语言中极力推崇的做法.Kot ...
- 快速了解C# 8.0中“可空引用类型(Nullable reference type)”语言特性
Visual C# 8.0中引入了可空引用类型(Nullable reference type),通过编译器提供的强大功能,帮助开发人员尽可能地规避由空引用带来的代码问题.这里我大致介绍一下可空引用类 ...
- C# 可空引用类型
可空引用类型是C#8.0计划新增的一个功能,不过已经发布了预览版本,今天我们来体验一下可空引用类型. 安装 您必须下载Visual Studio 2017 15.5预览版(目前最新发布版本是15.4) ...
- C# 8.0 可空(Nullable)给ASP.NET Core带来的坑
Nullable reference types(可为空引用类型) 可为空引用类型不讲武德 C#8.0 引入了"可为空引用类型"和"不可为空引用类型",使我们能 ...
- (基础篇) php中0与空 Null false的区别
<?php $test=0; if($test==''){ echo '<br />在php中,0即为空'; //被输出 } if($test===''){ echo '<br ...
- PHP中0、空、null和false的总结
php中很多人还不懂php中 0 , '' , null 和 false 之间的区别,这些区别有时会影响到数据判断的正确性和安全性,给程序的测试运行造成很多麻烦.另外在面试题中也会遇到这些问题,如下: ...
- 弱类型语言中的0和空字符串(''或"")以及字符串'0'
在弱类型语言(js/PHP)中, 当我们用==判断0和'0'以及空字符串(''或"")是否相等的时候, 返回的是true. 而且在PHP中, 当我们用==判断0和null是否相等的 ...
随机推荐
- C语言代码
//计算1/1+1/ (1+2) +1/ (1+2+3) +…+1/(1+2+…n)的值,要求小数点后保留6位,n从键盘输入 #include<stdio.h> main(){ ; ; i ...
- Docker快速入门(二)
上篇文章<Docker快速入门(一)>介绍了docker的基本概念和image的相关操作,本篇将进一步介绍image,容器和Dockerfile. 1 image文件 (1)Docker ...
- unity做游戏常用功能实现(一)多方向同时输入也能让物体正常移动
-------小基原创,转载请给我一个面子 网上有很多讲输入控制如何移动,但是多数都是讲单一按下,对于同时按下2个或2个以上按键并没有说明怎么解决,这里小基研究了一下方便大家 (如果你直接写input ...
- AUTOSAR的前期开源实现Arctic Core
AUTOSAR (AUTomotive Open System ARchitecture) is a worldwide development partnership of vehicle manu ...
- Java io使用简介
图:Java io概览图 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更 ...
- 12.Django思维导图
- 使用input file上传文件中onChange事件只触发一次问题
每次上传文件的时候,都会将当前的文件路径保存至$event.target.value中,当第二次选择文件时,由于两次$event.target.value相同,所以不会触发change事件. 解决方案 ...
- Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主
前戏 在正式开始axios讲解前,让我们先想想,如何对现有的$.ajax进行简单的封装,就可以直接使用原声Promise了? let axios = function(config){ return ...
- mybatis自定义代码生成器(Generator)——自动生成model&dao代码
花了两天的时间研究了下mybatis的generator大体了解了其生成原理以及实现过程.感觉generator做的非常不错,给开发者也留足了空间.看完之后在generator的基础上实现了自定义的生 ...
- PAT1003:Emergency
1003. Emergency (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue As an emerg ...