[C#]NULL——"不存在"的存在

thumbnail

I call it my billion-dollar mistake. It was the invention of the null reference in 1965.
–Tony Hoare, 2009

这是 NULL 的发明者 Tony Hoare 在 2009 年的一段演讲内容。许多使用 null 引用的语言中,写的最多重复的大概就是if(x!=null)

本文只涉及我熟悉的 C#语言,大概扯一下 NULL 的各种坑

NULL

Hoare 说引入 NULL 是因为实现更简单,而他的工作是确保一切引用都是安全的。也就是说,重点不是如何表示空值,而是避免使用空引用。但是显然if(x!=null)并不是最佳实现。首先容易忘 而且,由于 CPU 的分支预测,假如一个值是否为 NULL 是完全随机的,if 判空会使性能下降。

Null 的存在

在 C#8.0 之前,引用类型都是可为 null 的,而值类型可以通过?运算符使其成为Nullable Type可空类型(Nullable类型仍然是值类型),通过给值类型加?Nullable<T>的语法糖,它有一个HasValue属性和Value属性,如果HasValuefalse,尝试访问Value时会引发InvalidOperationException异常,而不是NullReferenceException,至于为什么后者那么不受待见…如果你还不知道,说明遇到的 null 还不够多!

也就是说,遇到Nullable<T>类型,要么判断HasValue,要么触发InvalidOperationException。Rust 的Option<T>枚举也很类似,但是由于它是枚举类型,你必须判断是否有值,这样完全不会引起 panic。

可为 null 的引用类型

这是 C#8.0 的新特性。说实话这个名字很怪,因为引用类型一直是 Nullable 的。既然单独提出 Nullable,那么其实是默认所有引用类型,如果不加修饰,也是不可为 null 的。但是如果直接给引用类型加?,编译器会给出警告:

1
warning CS8632: 只能在 "#nullable" 注释上下文内的代码中使用可为 null 的引用类型的注释。

说来惭愧,到了.NET5,我也没用过Nullable Reference Type(所以才有这篇文章)

要消除警告,需要使用#nullable enable#nullable restore(尽量使用restore 而不是 disable,因为项目默认设置可能会改变,这时候如果使用disable…出大问题);或者,给项目属性加一个

1
<Nullable>enable</Nullable>

VS2022+.NET6的一些新项目模板默认启用Nullable。这时应当为所有引用类型提供初始化值。这对POCO类很重要,因为许多数据库也是不推荐使用NULL的。
启用Nullable后,不为引用类型指定初始值会触发警告:

1
2
3
4
5
public class MyClass
{
int Id;
string Name;
}
1
2
warning CS8618: 在退出构造函数时,不可为 null 的 字段“Name”必须包含非 null 值。请考虑将 字段 声明为可以为 
null。

你可以将它声明为Nullable,或者,对于常见的集合(字符串姑且也算),直接为它指定一个空集合。

为什么应该使用Nullable

判断Nullable<T>.HasValueif(x != null) 其实没啥差别,除了前者异常处理更友好。

更好的做法是为引用类型指定初始值。如果说C#8.0之前的Nullable是为了给值类型创造null,现在的Nullable则是为了毁灭null。

在乎初始值占的那点内存?真的在乎那一点内存,还写什么C#!

Permalink: http://blog.artiga.top/2021/null-the-mistake/

本文采用CC BY-NC-SA 4.0许可

Comments