23

我曾经读到拥有可为空的类型是绝对的邪恶。我相信这是由创建它们的人写的一篇文章(在 Ada 中?)我相信这是这篇文章

无论如何,如果默认情况下像 C# 这样的语言使用不可为空的类型呢?您将如何替换 C# 或 Ruby 或任何其他null可接受值的常用语言中的一些常见习语?

4

11 回答 11

27

我不会直接宣布可空类型是邪恶的,而是假设:大多数语言将可空性移植到整个类型的类型上,而这两个概念实际上应该是正交的。

例如,所有非原始 Java 类型(以及所有 C# 引用类型)都可以为空。为什么?我们可以来回走,但最终我敢打赌答案归结为“这很容易”。Java 语言没有任何内在要求广泛的可空性。C++ 参考提供了一个很好的例子,说明如何在编译器级别消除空值。当然,C++ 有很多丑陋的语法,Java 明确地试图减少这些语法,所以一些好的特性最终与坏的一起被淘汰了。

C# 2.0 中的可空值类型朝着正确的方向迈出了一步——将可空性与不相关的类型语义,或者更糟糕的是,CLR 实现细节解耦——但它仍然缺少对引用类型进行相反处理的方法。(代码契约很棒,但它们并没有像我们在这里讨论的那样嵌入到类型系统中。)

许多函数式或其他晦涩的语言从一开始就将这些概念“直截了当”......但如果它们被广泛使用,我们就不会进行这个讨论......

回答你的问题:从现代语言中全面禁止 null 就像所谓的“十亿美元错误”一样愚蠢。有一些有效的编程结构可以很好地使用空值:可选参数,任何类型的默认/回退计算,其中合并运算符导致简洁的代码,与关系数据库的交互等。强迫自己使用标记值、NaN 等将是一种“治愈”远比疾病更糟糕。

也就是说,我暂时同意引用中表达的观点,只要我可以根据自己的经验进行详细说明:

  1. 需要空值的情况的数量比大多数人想象的要小
  2. 一旦将空值引入库或代码路径,就很难摆脱它们而不是添加它们。(所以不要让初级程序员一时兴起!)
  3. 可空的错误随着可变的生命周期而扩展
  4. 与#3相关:早早崩溃
于 2010-08-03T03:22:29.440 回答
24

我们会在(非常)少数实际需要允许空值的地方使用选项类型,并且我们会少很多模糊的错误,因为任何对象引用都可以保证指向适当类型的有效实例。

于 2010-08-03T03:08:42.173 回答
7

Haskell 是一种强大的语言,它没有无效性的概念。基本上,每个变量都必须初始化为非空值。如果您想表示一个“可选”变量(变量可能有值但可能没有),您可以使用特殊的“Maybe”类型。

在 Haskell 中实现这个系统比在 C# 中更容易,因为数据在 Haskell 中是不可变的,因此拥有稍后填充的空引用实际上没有任何意义。但是,在 C# 中,链表中的最后一个链接可能有一个指向下一个链接的空指针,该指针会在列表展开时填充。我不知道没有空类型的过程语言会是什么样子。

另外,请注意,上面的许多人似乎建议用特定于类型的逻辑“无”值(999-999-9999、“ NULL ”等)替换空值。这些值并不能真正解决任何问题,因为人们对 null 的问题是它们是一种特殊情况,但人们忘记为这种特殊情况编写代码。使用特定于类型的逻辑无值,人们仍然忘记为特殊情况编写代码,但他们避免了捕获此错误的错误,这是一件坏事。

于 2010-12-14T04:37:37.940 回答
4

我想你指的是这个演讲:“空参考:十亿美元的错误

于 2010-08-03T03:09:00.033 回答
4

您可以采用一个简单的规则:所有变量都被初始化(默认情况下,这可以被覆盖)为一个不可变的值,由变量的类定义。对于标量,这通常是某种形式的零。对于引用,每个类都将定义其“null”值是什么,并且引用将使用指向该值的指针进行初始化。

这将是 NullObject 模式的有效语言范围的实现:http ://en.wikipedia.org/wiki/Null_Object_pattern 所以它并没有真正摆脱空对象,它只是防止它们成为必须的特殊情况如此处理。

于 2010-08-03T03:25:57.457 回答
2

Null 不是问题,它是允许您编写访问可能为 null 的值的代码的语言。

如果该语言只需要首先检查任何指针访问或将其转换为不可为空的类型,那么 99% 的与空相关的错误都会消失。例如在 C++ 中

void fun(foo *f)
{
    f->x;                  // error: possibly null
    if (f)              
    {
        f->x;              // ok
        foo &r = *f;       // ok, convert to non-nullable type
        if (...) f = bar;  // possibly null again
        f->x;              // error
        r.x;               // ok
    }
}

可悲的是,这不能对大多数语言进行改进,因为它会破坏很多代码,但对于一种新语言来说是相当合理的。

于 2011-09-21T18:57:28.747 回答
2

Tcl 是一种语言,它不仅没有 null 的概念,而且 null 的概念本身与语言的核心不一致。在 tcl 中,我们说:'一切都是字符串'。它的真正含义是 tcl 具有严格的值语义(恰好默认为字符串)。

那么 tcl 程序员用什么来表示“无数据”呢?主要是空字符串。在某些情况下,空字符串可以表示数据,那么它通常是以下之一:

  1. 无论如何都要使用空字符串——大多数时候它对最终用户没有影响。

  2. 使用您知道数据流中不存在的值 - 例如字符串"_NULL_"或数字9999999或我最喜欢的 NUL 字节"\0"

  3. 使用包装在值周围的数据结构 - 最简单的是列表(其他语言称为数组)。一个元素的列表表示该值存在,零元素表示为空。

  4. 测试是否存在变量 - [info exists variable_name]

有趣的是,Tcl 并不是唯一具有严格值语义的语言。C 也有严格的值语义,但值的默认语义恰好是整数而不是字符串。

哦,差点忘了还有一个:

一些库使用数字 2 的变体,允许用户指定“无数据”的占位符是什么。基本上它允许您指定默认值(如果您不指定默认值通常默认为空字符串)。

于 2010-08-03T03:45:11.820 回答
1

实际上,在任何首先允许指针或对象引用的强大编程语言中,都会出现代码能够访问没有任何初始化代码在其上运行的指针的情况。可以保证这些指针将被初始化为某个静态值,但这似乎并不是非常有用。如果机器具有捕获对未初始化变量(无论是指针还是其他东西)的访问的一般方法,那比特殊情况下的空指针更好,但除此之外,我看到的最大的空相关错误发生在允许使用空指针进行算术的实现中. 将 5 添加到 (char*)0 不应产生指向地址 5 的字符指针;它应该触发一个错误(如果适合创建指向绝对地址的指针,

于 2010-12-14T04:26:50.647 回答
1

如果没有 NULL,我们会怎么做?发明它!:-) 如果您正在寻找一个带内指针值来表达实际上不是指针,那么您不必成为火箭科学家就可以使用 0 。

于 2011-09-11T20:04:39.147 回答
1

如其他答案所示,我们将创建各种奇怪的构造来传达对象“无效”或“不存在”的信息。null可以很好地传达的信息。

  • Null Object 模式有其缺点,正如我在此处解释的那样。
  • 特定于域的空值。这会迫使您检查幻数,这很糟糕
  • 集合包装器,其中一个空集合意味着“没有价值”。可空null包装器会更好,但这与检查或使用空对象模式没有太大区别。

就个人而言,我会编写一些 C# 预处理器,允许我使用null. 然后,这将映射到某个对象,每当对其调用方法时dynamic,该对象就会抛出一个。NullReferenceException

早在 1965 年,空引用可能看起来像是一个错误。但是现在,有了各种警告我们空引用的代码分析工具,我们不必那么担心。从编程的角度来看null是一个非常有价值的关键字。

于 2010-08-03T07:49:08.407 回答
0

我们使用

  1. 鉴别器。一个额外的属性或标志或指示符,表示一个值为“null”并且必须被忽略。

  2. 特定于域的空值。一个特定的值——在允许的域内——被解释为“忽略这个值”。例如,社会安全号码 999-99-9999 可能是特定于域的空值,表示 SSN 未知或不适用。

于 2010-08-03T03:09:32.527 回答