最近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#the-null-forgiving-operator

https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-reference-types.md#setting-project-level-nullable-context

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

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

C#8.0可空引用类型的使用注意要点的更多相关文章

  1. C#8.0—非空引用类型

    非空引用类型--C#8.0 原文地址:https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/?utm_sourc ...

  2. C#8.0 可空引用类型

    介绍 我们的项目代码运行时最频繁的错误之一就是 System.NullReferenceException 异常,c#8.0增加的可为空引用类型就是用来帮助开发者降低甚至消除NULL异常.我们需要注意 ...

  3. 迫不及待地体验了一把 C#8.0 中的可空引用类型(Nullable Reference)

    在我之前的一篇博客 NullReferenceException,就不应该存在! 中,我吐槽了 C# 中 null 的弊端以及避免 null 的方法:事实上这本都是现代高级语言中极力推崇的做法.Kot ...

  4. 快速了解C# 8.0中“可空引用类型(Nullable reference type)”语言特性

    Visual C# 8.0中引入了可空引用类型(Nullable reference type),通过编译器提供的强大功能,帮助开发人员尽可能地规避由空引用带来的代码问题.这里我大致介绍一下可空引用类 ...

  5. C# 可空引用类型

    可空引用类型是C#8.0计划新增的一个功能,不过已经发布了预览版本,今天我们来体验一下可空引用类型. 安装 您必须下载Visual Studio 2017 15.5预览版(目前最新发布版本是15.4) ...

  6. C# 8.0 可空(Nullable)给ASP.NET Core带来的坑

    Nullable reference types(可为空引用类型) 可为空引用类型不讲武德 C#8.0 引入了"可为空引用类型"和"不可为空引用类型",使我们能 ...

  7. (基础篇) php中0与空 Null false的区别

    <?php $test=0; if($test==''){ echo '<br />在php中,0即为空'; //被输出 } if($test===''){ echo '<br ...

  8. PHP中0、空、null和false的总结

    php中很多人还不懂php中 0 , '' , null 和 false 之间的区别,这些区别有时会影响到数据判断的正确性和安全性,给程序的测试运行造成很多麻烦.另外在面试题中也会遇到这些问题,如下: ...

  9. 弱类型语言中的0和空字符串(''或"")以及字符串'0'

    在弱类型语言(js/PHP)中, 当我们用==判断0和'0'以及空字符串(''或"")是否相等的时候, 返回的是true. 而且在PHP中, 当我们用==判断0和null是否相等的 ...

随机推荐

  1. FFMpeg编译之路

    为了编译这个东西,快折腾了一个星期了.期间经历了很多痛苦的过程,今天我把整个过程,以及在这个过程的感悟写下来,以备日后查看,也希望能帮到一些像我一样的兄弟姐妹. 在这一个星期里前前后后加起来总共使用了 ...

  2. 基于Kurento的WebRTC移动视频群聊技术方案

    说在前面的话:视频实时群聊天有三种架构: Mesh架构:终端之间互相连接,没有中心服务器,产生的问题,每个终端都要连接n-1个终端,每个终端的编码和网络压力都很大.群聊人数N不可能太大. Router ...

  3. Go语言开发区块链只需180行代码

    区块链开发用什么语言?通过本文你将使用Go语言开发自己的区块链(或者说用go语言搭建区块链).理解哈希函数是如何保持区块链的完整性.掌握如何用Go语言编程创造并添加新的块.实现多个节点通过竞争生成块. ...

  4. Java学习导航

    由于最近在系统的重新学习Java,为了便于日后复习,给个人博客中Java内容做一个目录. Java基础:Java虚拟机(JVM) Java基础:内存模型 Java基础:JVM垃圾回收算法 Java基础 ...

  5. servlet什么时候被实例化?【转】

    如果没有设置loadOnStartup,则第一次请求的时候实例化 分三种情况:loadOnStartup < 0 即负数的情况下,web容器启动的时候不做实例化处理,servlet首次被调用时做 ...

  6. springmvc 请求经过controller后静态资源无法访问的问题

    经过RequestMapping(“xx”)后 转发请求时会在url里面附带地址, 导致访问静态资源文件失败, 解决办法是在 spring-mvc.xml文件中加上 <mvc:default-s ...

  7. python中@classmethod @staticmethod区别

    Python中3种方式定义类方法, 常规方式, @classmethod修饰方式, @staticmethod修饰方式. class A(object): def foo(self, x): prin ...

  8. Java程序算法设计视频分享,需要的来

    每年都会有人说,IT行业饱和了,根本就找不到工作,其实,我想说的是,不是工作难找,而是你自己不够好! 前几天看到一CEO在微博上吐槽: 前几天招一算法工程师我们给了8万月薪*14+奖金,人家去阿里拿5 ...

  9. scrapy中 Mongo的存储

    import pymongo MONGO_HOST = 'localhost' MONGO_PORT = 27017 MONGO_DB = 'today' MONGO_COLL = 'mm' clas ...

  10. java thread yield 的设计目的是什么?

    如题,java thread yield 的设计目的是什么?有什么实际应用场景吗? Ps:它的作用是理解的,和 join 等的区别也理解.就是个人感觉这个设计有点鸡肋(可能是个人读书太少...) It ...