为什么.NET中存在null?

时间:2011-03-01 00:05:53

标签: c# .net performance clr type-systems

为什么.NET中的值为null?这是否优于保证所有内容都有值且没有任何调用为空?

任何人都知道这些方法的名称是什么?

无论哪种方式,我对此都知之甚少,但在简单性方面,即在消除空检查方面,并且能够编写更多不具备的简化算法分支出来进行检查。

每种风格在性能,简洁性,平行性,面向未来等方面的优缺点是什么。

11 个答案:

答案 0 :(得分:37)

我们有Tony Hoare,这是早期的先驱者,感谢Algol对此表示感谢。他很后悔:

  

我称之为十亿美元的错误。   这是null的发明   在1965年参考。当时,我是   设计第一个综合类型   用于对象中引用的系统   面向语言(ALGOL W)。我的目标   是为了确保所有的使用   引用应绝对安全,   检查自动执行   由编译器。但我无法抗拒   投入无效的诱惑   参考,仅仅因为它是如此   易于实施。这导致了   无数错误,漏洞,   和系统崩溃,有   可能造成了十亿美元的损失   最后四十年的痛苦和伤害   年。

我认为十亿是一个低球数。

答案 1 :(得分:24)

尽管没有null的世界很吸引人,但它确实给许多现有的模式和结构带来了很多困难。例如,考虑以下构造,如果null不存在则需要进行重大更改

  1. 创建引用类型数组ala:new object[42]。在现有的CLR世界中,数组将填充null,这是非法的。数组语义需要在这里改变一下
  2. 仅当default(T)是值类型时才会使T有用。不允许在引用类型或无约束泛型上使用它
  3. 需要禁止作为引用类型的结构中的字段。今天可以在CLR中对值类型进行0初始化,这样可以方便地使用null填充引用类型的字段。在非空世界中这是不可能的,因此需要禁止类型为struct的引用类型的字段
  4. 上述问题都不是无法解决的,但它们确实会带来真正挑战开发人员如何考虑编码的变化。我个人希望C#和.Net的设计是为了消除null,但不幸的是它不是,我想像上面的问题有点与它有关。

答案 2 :(得分:9)

这让我想起詹姆斯伯克的“连线”系列剧集,其中僧侣正在将阿拉伯语抄写成拉丁语,并且首次遇到零数字。罗马算术没有零的表示,但阿拉伯语/ aramaic算法确实如此。 “为什么我们要写一封信来表示什么?”争辩天主教僧侣。 “如果没什么,我们应该什么都不写!”

幸运的是,对于现代社会来说,他们失去了争论并学会了在数学中写出零位数。 ;>

Null只表示没有对象。有些编程语言本身没有“null”,但是大多数编程语言仍然有代表缺少合法对象的东西。如果你丢弃“null”并将其替换为名为“EmptyObject”或“NullNode”的东西,那么它仍然是一个带有不同名称的null。

如果你删除编程语言表示不引用合法对象的变量或字段的能力,也就是说,你要求每个变量和字段总是包含一个真实有效的对象实例,那你就做了一些非常好的事情。有用且高效的数据结构笨拙且效率低下,例如构建链表。程序员不必使用null来指示链表的结尾,而是强制发明“假”对象实例作为列表终端,除了表示“此处没有”之外什么都不做。

在这里深入研究存在主义,但是:如果你可以表现出某种东西的存在,那么是否还有一种基本的需要能够代表它的存在呢?

答案 3 :(得分:6)

推测他们在.NET中存在null,因为它(C#)遵循C ++ / Java脚步(并且在最近的版本中才开始分支出来) )和VB / J ++(成为VB.NET / J#)已经有了“没有”值的概念 - 也就是说,.NET由于而没有null 可能的内容。

在某些语言中没有null 的概念 - null可以完全替换为类似Maybe的类型 - 有某事物(物体)或者没什么(但这不是null没有办法从“没有”中获得“没什么”!

在Scala中使用选项:

val opt = Some("foo") // or perhaps, None
opt match {
   case Some(x) => x.toString() // x not null here, but only by code-contract, e.g. Some(null) would allow it.
   case _ => "nothing :(" // opt contained "Nothing"
}

这是通过Haskell中的语言设计完成的null根本不可能!)和通过库支持和谨慎使用等来完成Scala,如上所示。 (Scala支持null - 可以说是Java / C#interop - 但是可以编写Scala代码而不使用这个事实,除非允许null“泄漏”。

修改:查看Scala: Option PatternScala: Option Cheat CheetSO: Use Maybe Type in Haskell。大多数时候在Haskell谈论Maybe会带来Monads的主题。我不会声称理解它们,但是here is a link along with usage of Maybe

快乐的编码。

答案 4 :(得分:5)

好的,现在用C#-without-null

的神奇单词换行
class View
{
    Model model;        

    public View(Model model)
    {
        Console.WriteLine("my model : {0}, thing : {1}", this.model, this.model.thing);
        this.model = model;
    }
}

控制台上印有什么?

  • 没有任何关于访问未初始化对象的例外:Ok称之为NullReferenceException,那就是当前世界。
  • 它没有构建,用户在声明模型时需要指定一个值,请参阅最后一个项目符号,因为它会创建相同的结果。
  • 模型的一些默认值和事物的一些默认值:Ok之前使用null我们至少有一种方法可以知道实例是否正确现在编译器生成了奇怪的外观,它们不包含任何内容但仍然是作为模型对象无效......
  • 由对象类型定义的东西:更好,因为我们可以为每个对象定义一个特定的无效状态,但现在每个可能无效的对象需要独立实现它以及调用者识别此状态的方法...

所以基本上对我来说,似乎没有解决任何问题来删除null状态,因为仍然需要管理可能无效的状态......

哦,购买接口默认值的方式?哦,一个抽象类,当在抽象类中定义的默认值上调用方法但是调用另一个抽象方法时会发生什么? .... ....为什么哦为什么一劳永逸地使模型复杂化,这又是多重继承问题!

一种解决方案是完全改变语法以获得一个完整的功能,其中null世界不会退出,只有当你希望它们存在时才是Maybes ...但它不是一个C语言和多个.Net的范式将会丢失。

可能缺少的是一个null传播的运算符,当model为null时,它可以返回null model.Thing,如model.?.Thing


哦,好好回答你的问题:

  • 当前的类库在Microsoft-Java崩溃之后发展而且C#被构建为“更好的Java”,因此更改类型系统以删除空引用将是一个很大的变化。他们已经设法引入价值类型并删除了手动拳击!
  • 正如值类型介绍显示microsoft对速度有很多想法...例如,所有类型的默认值映射到零填充的事实对于快速数组初始化非常重要。否则,初始化参考值数组将需要特殊的威胁。
  • 如果没有null,就不可能与C互操作,因此至少在MSIL级别和不安全的区域中,他们需要被允许生存。
  • 微软希望使用VB6 ++删除Nothing的框架,因为在VB中调用它会彻底改变语言,用户从VB6切换到VB.Net需要花费数年的时间才能改变范式对这种语言来说是致命的。

答案 5 :(得分:3)

嗯,(值类型变量)只能是null,因为可空类型是在Fx2中引入的。

但我想你的意思是:

为什么引用可以是null

这是参考文献有用性的一部分。考虑一个Tree或LinkedList,如果没有null,它们将无法(无法结束)。

您可以提出更多示例,但主要是null来模拟“可选”属性/关系的概念。

答案 6 :(得分:3)

歇斯底里的葡萄干。

这是来自C级语言的宿醉,您可以使用显式指针操作。现代的声明性语言(Mercury,Haskell,OCaml等)非常幸福地得到了没有空值。在那里,每个值都必须明确构建。 'null'思想是通过'option'类型处理的,它们有两个值,'no'(对应于null)和'yes(x)'(对应于值为x的非null)。您必须解压每个选项值以决定要做什么,因此:没有空指针引用错误。

语言中没有空值会让你感到非常悲痛,这种想法仍然存在于高级语言中真的很遗憾。

答案 7 :(得分:2)

我也不熟悉替代方案,但是我没有看到Object.Empty和null之间的区别,除了null之外,当你的代码试图访问对象时,会让你知道出了什么问题,对象是Object.Empty允许处理继续。有时你想要一个行为,有时你想要另一个行为。区分null和Empty是一个很有用的工具。

答案 8 :(得分:1)

表示虚无概念,因为0不合适。

现在,您可以通过定义可空类型为任何值类型提供Null值。

我认为我们不能总是为变量赋值,因为首先我们必须将它默认为某个值,这就是为什么特定值优于其他值的问题。

答案 9 :(得分:1)

许多人可能无法在没有空值的情况下绕过编码,如果C#没有空值,我怀疑它是否已经达到了它的范围。

话虽如此,一个不错的选择是,如果你想允许一个可以为空的引用,那么引用必须是明确可为空的,就像值类型一样。

例如,

Person? person = SearchForPersonByFirstName("Steve");
if (person.HasValue)
{
    Console.WriteLine("Hi, " + person.Value.FullName);
}

不幸的是,当C#1.0问世时,没有Nullable的概念;这是在C#2.0中添加的。强制引用具有值会破坏旧程序。

答案 10 :(得分:1)

null只是引用类型的默认值的名称。如果不允许null,那么“没有价值”的概念就不会消失,你只会以不同的方式表示它。除了具有特殊名称之外,该默认值在被滥用的情况下也具有特殊语义 - 即,如果您将其视为有值,则实际上没有。

如果没有null

  1. 您需要为“没有值”时使用的类型定义哨兵默认值。作为一个简单的例子,考虑前向单链表的最后一个节点。
  2. 您需要为在所有这些标记值上执行的操作定义语义。
  3. 运行时会有额外的开销来处理sentinel值。在现代虚拟机(例如.NET和Java使用的虚拟机)中,在大多数函数调用和解除引用之前没有空检查的开销,因为CPU提供了处理这种情况的特殊方法,并且CPU针对以下情况进行了优化:引用是非空的(例如,分支预测)。
  4. 总结:

    • 名称会改变,但概念不会改变。
    • 在开发人员和开发人员文档中,为没有“无价值”的情况定义和传递默认值和语义的负担。
    • 在现代虚拟机上执行的代码将面临许多算法的代码膨胀和显着的性能下降。

    Tony Hoare描述的null问题通常是由于在现代虚拟机之前,运行时系统几乎没有像今天这样干净处理误用的null值。滥用指针/引用仍然是一个问题,但是在使用.NET或Java时追踪问题往往比以前更容易 。下进行。