C# 8.0可空引用类型的使用注意记录

所属分类: 软件编程 / C#教程 阅读数: 121
收藏 0 赞 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://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/nullable-reference-types-specification

https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-reference-types.md

https://www.youtube.com/watch?v=VdC0aoa7ung

https://stackoverflow.com/questions/54852880/what-is-the-difference-between-nullablecontextoptions-and-nullablereferencetypes

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

更多精彩内容其他人还在看

c#开发word批量转pdf源码分享

已经安装有Office环境,借助一些简单的代码即可实现批量Word转PDF,看下面的实例源码吧
收藏 0 赞 0 分享

c# xml API操作的小例子

这篇文章主要介绍了c# xml API操作的小例子,有需要的朋友可以参考一下
收藏 0 赞 0 分享

c#唯一值渲染实例代码

这篇文章主要介绍了c#唯一值渲染实例代码,有需要的朋友可以参考一下
收藏 0 赞 0 分享

淘宝IP地址库采集器c#代码

这篇文章主要介绍了淘宝IP地址库采集器c#代码,有需要的朋友可以参考一下
收藏 0 赞 0 分享

C#在后台运行操作(BackgroundWorker用法)示例分享

BackgroundWorker类允许在单独的专用线程上运行操作。如果需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用BackgroundWorker类方便地解决问题,下面看示例
收藏 0 赞 0 分享

c#文本加密程序代码示例

这是一个加密软件,但只限于文本加密,加了窗口控件的滑动效果,详细看下面的代码
收藏 0 赞 0 分享

c#生成站点地图(SiteMapPath)文件示例程序

这篇文章主要介绍了c#生成站点地图(SiteMapPath)文件的示例,大家参考使用
收藏 0 赞 0 分享

C# 键盘Enter键取代Tab键实现代码

这篇文章主要介绍了C# 键盘Enter键取代Tab键实现代码,有需要的朋友可以参考一下
收藏 0 赞 0 分享

C# WinForm导出Excel方法介绍

在.NET应用中,导出Excel是很常见的需求,导出Excel报表大致有以下三种方式:Office PIA,文件流和NPOI开源库,本文只介绍前两种方式
收藏 0 赞 0 分享

C#串口通信程序实例详解

在.NET平台下创建C#串口通信程序,.NET 2.0提供了串口通信的功能,其命名空间是System.IO.Ports,创建C#串口通信程序的具体实现是如何的呢?让我们开始吧
收藏 0 赞 0 分享
查看更多