没有NULL我们会做什么?

时间:2010-08-03 03:05:20

标签: language-agnostic null language-design non-nullable

我曾经读过,拥有可空类型是绝对邪恶的。我相信这是由创造它们的人写的一篇文章(在Ada?)我相信this is the article

无论如何,那么如果默认情况下像C#这样的语言使用了非可空类型呢?你会如何替换C#或Ruby中的一些常用习语或null是可接受值的任何其他常用语言?

11 个答案:

答案 0 :(得分:26)

我没有直截了当地声明可空类型是邪恶的,我认为:大多数语言都将可空性嫁接到各种类型上,而这两个概念应该是正交的

例如,所有非原始Java类型(以及所有C#引用类型)都可以为空。为什么?我们可以回去&第四,但最终我敢打赌答案归结为“这很容易”。 Java语言没有任何内在要求广泛的可空性。 C ++参考提供了一个很好的例子,说明如何在编译器级别驱除空值。当然,C ++有一些非常丑陋的语法,Java明确地试图减少它,所以一些好的功能最终会出现在切割底线上。

C#2.0中的可空值类型向正确的方向迈出了一步 - 将可空性与无关类型语义分离,或者更糟糕的是,CLR实现细节 - 但它仍然缺少与引用类型相反的方法。 (代码合同很好,但是它们并没有像我们在这里讨论的那样嵌入到类型系统中。)

许多功能性或其他模糊语言从一开始就让这些概念“直截了当”......但如果它们被广泛使用,我们就不会讨论......

回答你的问题:禁止现代语言中的空白批发,与所谓的“十亿美元错误”一样愚蠢。有一些有效的编程结构,其中空值很好:可选参数,任何类型的默认/回退计算,其中coalesce运算符导致简洁代码,与关系数据库的交互等。强制自己使用sentinel值,NaN等将是比疾病更糟糕的“治愈”。

那就是说,我暂时会同意引文中表达的情绪,只要我可以详细说明我的经验:

  1. 需要空值的情况小于大多数人认为的
  2. 一旦你将空值引入库或代码路径中,它的很多比添加它们更难以摆脱它们。 (所以不要让初级程序员一时兴起!)
  3. 可以使用可变生命周期的可空错误缩放
  4. 与#3相关:早期崩溃

答案 1 :(得分:23)

我们将option types用于允许空值实际需要的(非常)少数几个地方,并且我们有更少的模糊错误,因为任何对象引用都将保证指向有效适当类型的实例。

答案 2 :(得分:7)

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

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

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

答案 3 :(得分:4)

我认为你指的是这句话:“Null References: The billion dollar mistake

答案 4 :(得分:4)

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

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

答案 5 :(得分:2)

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

那么tcl程序员用什么来表示“无数据”?大部分都是空字符串。在某些情况下,空字符串可以表示数据,然后通常是以下之一:

  1. 无论如何都要使用空字符串 - 大部分时间它对最终用户没有任何影响。

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

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

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

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

    哦,差点忘了另一个:

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

答案 6 :(得分:2)

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

如果语言只需要检查任何指针访问权限或首先将其转换为非可空类型,则99%的null相关错误将消失。例如。在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
    }
}

可悲的是,这不能用于大多数语言的改装,因为它会破坏很多代码,但对于新语言来说是非常合理的。

答案 7 :(得分:1)

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

答案 8 :(得分:1)

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

答案 9 :(得分:0)

我们使用

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

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

答案 10 :(得分:0)

我们会创建各种奇怪的结构来传达对象“无效”或“不在那里”的信息,如其他答案所示。 null可以传达的信息。

  • Null Object pattern有其缺点,我explained here
  • 特定于域的空值。这会强制您检查幻数,which is bad
  • 集合包装器,其中空集合意味着“没有价值”。 Nullable包装器会更好,但这与检查null或使用Null对象模式没什么不同。

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

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