关于null:为什么我不应该总是在C#中使用可空类型

Why shouldn't I always use nullable types in C#

自从.NET 2.0中引入这个概念以来,我一直在寻找关于这个问题的一些很好的指导。

为什么我要在C中使用不可为空的数据类型?(一个更好的问题是,为什么我不在默认情况下选择可以为空的类型,并且只在显式意义上使用不可以为空的类型。)

选择一个可以为空的数据类型而不是其不可以为空的对等数据类型是否会对性能造成"重大"影响?

我更喜欢检查我的值是否为空,而不是guid.empty、string.empty、datetime.minvalue、<=0等,并且通常使用可以为空的类型。我之所以不经常选择可以为空的类型,唯一的原因是我后脑勺的痒感,让我觉得这不仅仅是向后兼容,它迫使我额外的"?"字符以显式允许空值。

是否有人总是(最总是)选择可以为空的类型而不是不可以为空的类型?

谢谢你的时间,


您不应该总是使用可空类型的原因是,有时您可以保证值将被初始化。您应该尝试设计您的代码,以便尽可能经常这样做。如果一个值不可能未初始化,那么就没有理由认为空值应该是它的合法值。作为一个非常简单的例子,考虑一下:

1
2
List<int> list = new List<int>()
int c = list.Count;

这总是有效的。没有可能的方法可以不初始化c。如果它被转换成一个int?,您实际上会告诉代码的读者"这个值可能是空的。使用前一定要检查。但是我们知道这永远不会发生,那么为什么不在代码中公开这个保证呢?

在值是可选的情况下,您是绝对正确的。如果我们有一个函数可以返回字符串,也可以不返回字符串,那么返回空值。不要返回string.empty()。不要返回"魔法值"。

但并非所有值都是可选的。使所有内容都是可选的会使代码的其余部分更加复杂(它添加了另一个必须处理的代码路径)。

如果您可以特别保证这个值总是有效的,那么为什么要丢弃这个信息呢?这就是通过将其设置为可以为空的类型来实现的。现在该值可能存在,也可能不存在,任何使用该值的人都必须处理这两种情况。但是你知道一开始只有一种情况是可能的。所以,请您的代码用户帮个忙,并在代码中反映这一事实。然后,代码的任何用户都可以依赖有效的值,他们只需要处理一个案例,而不需要处理两个案例。


因为总是需要检查是否可以为空的类型是null,这是不方便的。

显然,在某些情况下,值是真正可选的,在这些情况下,使用可为空的类型而不是幻数等是有意义的,但在可能的情况下,我会尽量避免使用它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
// nice and simple, this will always work
int a = myInt;

// compiler won't let you do this
int b = myNullableInt;

// compiler allows these, but causes runtime error if myNullableInt is null
int c = (int)myNullableInt;
int d = myNullableInt.Value;    

// instead you need to do something like these, cumbersome and less readable
int e = myNullableInt ?? defaultValue;
int f = myNullableInt.HasValue ? myNullableInt : GetValueFromSomewhere();


你好像有两个不同的问题…

Why would I ever want to use non-nullable data types in C#?

很简单,因为编译器保证您所依赖的值类型数据实际具有值!

Why wouldn't I choose nullable types by default, and only use non-nullable types when that explicitly makes sense?

正如Joel已经提到的,如果类型是引用类型,则它只能为空。编译器保证值类型具有值。如果您的程序依赖一个变量来有一个值,那么这就是您不选择可以为空的类型所希望的行为。

当然,当您的数据来自任何不是您的程序的地方时,所有的赌注都将取消。最好的例子来自数据库。数据库字段可以是null,因此您希望程序变量模拟该值,而不仅仅是创建一个"magic"值(即-1、0或其他任何表示null的值)。使用可以为空的类型执行此操作。


我认为语言设计者认为"引用类型在默认情况下可以为空"是一个错误,而不可以为空是唯一合理的默认值,您应该选择为空。(在许多现代函数语言中就是这样的)"空"通常是一堆麻烦。


尽管可以方便地将空值用作"尚未初始化"或"未指定"值,但它们会使代码更加复杂,主要是因为您正在重载null的含义以及变量(数字或空值vs.just-a-number)。

许多数据库设计人员和SQL数据库程序员都喜欢使用空值,但在考虑问题时只需稍作改变,就可以去掉空值,实际上拥有更简单、更可靠的代码(例如,不必担心NullReferenceExceptions)。

实际上对"T"的需求量很大。使任何引用类型不可为空的运算符,类似于"t"的用法。使值类型可以为空,C的发明者anders hejlsberg希望他包含了这种能力。

还有一个问题,为什么在C语言和Java中存在"null"呢?


我倾向于在任何有意义的地方使用可以为空的类型——我不关心性能,除非它是一个问题,然后我将修复它存在的几个方面,并用它来完成。

然而,我也发现,通常情况下,我的大多数值最终都是不可空的。事实上,有很多次我真的想要一个NotNullable,我可以在引用类型中使用它来发现一个空问题,当我得到空值时,而不是稍后我试图使用它时。


只有在数据库表中的某个字段绝对要求应用程序在某个时刻发送或接收一个空值的情况下,才应该使用可以为空的类型。即使在这种情况下,也应该始终尝试找到一种方法来绕过使用可以为空的类型。布尔并不总是你最好的朋友。