为什么 .NET 中存在 null?

发布于 2024-10-19 19:02:33 字数 200 浏览 5 评论 0原文

为什么 .NET 中的值可以为 null?这是否优于保证所有东西都有值且没有东西为空的保证?

有人知道这些方法的名称是什么吗?

不管怎样,我对此不是很了解,但就简单性而言,并不是所有东西都有一个值会让事情变得更容易,即消除空检查,并能够编写更精简的算法,而不必分支用于检查。

每种风格在性能、简单性、并行性、面向未来等方面都有哪些优缺点?

Why can values be null in .NET? Is this superior to having a guarantee where everything would have a value and nothing call be null?

Anyone knows what each of these methodologies are called?

Either way, I am not very knowledgeable on this, but wouldn't having a value for everything makes things easier, in terms of simplicity, i.e. eliminating null checks, and being able to write more streamlined algorithms that doesn't have to branch out for checks.

What are the pros and cons of each style in terms of performance, simplicity, parallellism, future-proofing, etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

櫻之舞 2024-10-26 19:02:33

我们有 Tony Hoare,他是 Algol 的早期先驱为此表示感谢。他颇为遗憾:

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

我认为十亿是一个偏低的数字。


更新:C# 版本 8 和 .NETCore 对于此问题有一个不错的解决方案,请查看 不可为 null 的引用类型

We've got Tony Hoare, an early pioneer that worked on Algol to thank for that. He rather regrets it:

I call it my billion-dollar mistake.
It was the invention of the null
reference in 1965. At that time, I was
designing the first comprehensive type
system for references in an object
oriented language (ALGOL W). My goal
was to ensure that all use of
references should be absolutely safe,
with checking performed automatically
by the compiler. But I couldn't resist
the temptation to put in a null
reference, simply because it was so
easy to implement. This has led to
innumerable errors, vulnerabilities,
and system crashes, which have
probably caused a billion dollars of
pain and damage in the last forty
years.

A billion is a low-ball number, I think.


UPDATE: C# version 8 and .NETCore have a decent solution for this problem, check out non-nullable reference types.

兮子 2024-10-26 19:02:33

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

  1. 创建引用类型 ala 的数组:new object[42]。在现有的 CLR 世界中,数组将用 null 填充,这是非法的。数组语义在这里需要进行相当大的改变,
  2. 只有当 T 是值类型时,它才使 default(T) 有用。不允许在引用类型或不受约束的泛型上使用它
  3. 。结构中作为引用类型的字段需要被禁止。现在,在 CLR 中可以将值类型初始化为 0,这样可以方便地使用 null 填充引用类型的字段。这在非空世界中是不可能的,因此需要禁止类型为结构中引用类型的字段。

上述问题都不是无法解决的,但它们确实会导致真正挑战开发人员如何思考编码的变化。就我个人而言,我希望 C# 和 .Net 的设计能够消除 null,但不幸的是事实并非如此,我想像上面这样的问题与此有一定关系。

As appealing as a world without null is, it does present a lot of difficulty for many existing patterns and constructs. For example consider the following constructs which would need major changes if null did not exist

  1. Creating an array of reference types ala: new object[42]. In the existing CLR world the arrays would be filled with null which is illegal. Array semantics would need to change quite a bit here
  2. It makes default(T) useful only when T is a value type. Using it on reference types or unconstrained generics wouldn't be allowed
  3. Fields in a struct which are a reference type need to be disallowed. A value type can be 0-initialized today in the CLR which conveniently fills fields of reference types with null. That wouldn't be possible in a non-null world hence fields whos type are reference types in struct's would need to be disallowed

None of the above problems are unsolvable but they do result in changes that really challenge how developers tend to think about coding. Personally I wish C# and .Net was designed with the elimination of null but unfortunately it wasn't and I imagine problems like the above had a bit to do with it.

知足的幸福 2024-10-26 19:02:33

这让我想起詹姆斯·伯克的《连线》系列中的一集,僧侣们将阿拉伯语抄写成拉丁语,第一次遇到了零数字。罗马算术没有零的表示,但阿拉伯/阿拉姆算术有。 “为什么我们一定要写信表明什么?”天主教修士们争论道。 “如果没什么,我们就什么也不写!”

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

Null 只是表示不存在对象。有些编程语言本身没有“null”,但大多数语言仍然有一些东西来表示合法对象的不存在。如果你扔掉“null”并将其替换为“EmptyObject”或“NullNode”,它仍然是一个 null,只是名称不同。

如果你删除了编程语言表示不引用合法对象的变量或字段的能力,也就是说,你要求每个变量和字段始终包含一个真实有效的对象实例,那么你就做出了一些非常有用和高效的东西。数据结构笨拙且低效,例如构建链表。程序员被迫发明“假”对象实例来充当列表终端,而不是使用 null 来指示链表的末尾,该实例除了指示“这里什么都没有”之外什么都不做。

在这里深入研究存在主义,但是:如果你可以代表某物的存在,那么是否也有基本的需要能够代表它的不存在?

This reminds me of an episode of James Burke's "Connections" series where monks were transcribing arabic to latin and first encountered a zero digit. Roman arithmetic did not have a representation for zero, but arabic/aramaic arithmetic did. "Why do we have to write a letter to indicate nothing?" argued the Catholic monks. "If it is nothing, we should write nothing!"

Fortunately for modern society, they lost the argument and learned to write zero digits in their maths. ;>

Null simply represents an absence of an object. There are programming languages which do not have "null" per se, but most of them do still have something to represent the absence of a legitimate object. If you throw away "null" and replace it with something called "EmptyObject" or "NullNode", it's still a null just with a different name.

If you remove the ability for a programming language to represent a variable or field that does not reference a legitimate object, that is, you require that every variable and field always contain a true and valid object instance, then you make some very useful and efficient data structures awkward and inefficient, such as building a linked list. Instead of using a null to indicate the end of the linked list, the programmer is forced to invent "fake" object instances to serve as list terminals that do nothing but indicate "there's nothing here".

Delving into existentialism here, but: If you can represent the presence of something, then isn't there a fundamental need to be able to represent the absence of it as well?

冬天旳寂寞 2024-10-26 19:02:33

推测它们在.NET中存在null,因为它(C#)追随了C++/Java的脚步(并且只在更新的版本中开始分支) )和 VB/J++(后来成为 VB.NET/J#)已经有了“无”值的概念——也就是说,.NET 有 null 因为曾经而不是因为它可能是什么。

在某些语言中,没有 null 的概念 - null 可以完全替换为 Maybe 之类的类型 - - 有 Something(对象)或 Nothing(但这不是 null没有办法从 Maybe 中得到“Nothing”!

在 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# 互操作——但是可以在不使用这个事实的情况下编写 Scala 代码,除非允许“泄漏”null) 。

编辑:请参阅Scala:选项模式Scala:选项 Cheat CheetSO:在 Haskell 中使用 Maybe Type。大多数时候在 Haskell 中谈论 Maybe 都会提到 Monad 的话题。我不会声称理解它们,但是这里有一个链接以及使用也许吧。

快乐编码。

I speculate that their exists null in .NET because it (C#) followed in the C++/Java foot-steps (and has only started to branch-out in more recent versions) and VB/J++ (which became VB.NET/J#) already had the notion of "nothing" values -- that is, .NET has null because of what was and not because of what it could have been.

In some languages there is no notion of null -- null can be completely replaced with a type like Maybe -- there is Something (the object) or Nothing (but this is not null! There is no way to get the "Nothing" out of an Maybe!)

In Scala with Option:

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"
}

This is done by language design in Haskell (null not possible ... at all!) and by library support and careful usage such as in Scala, as shown above. (Scala supports null -- arguably for Java/C# interop -- but it is possible to write Scala code without using this fact unless null is allowed to "leak" about).

Edit: See Scala: Option Pattern, Scala: Option Cheat Cheet and SO: Use Maybe Type in Haskell. Most of the time talking about Maybe in Haskell brings up the topic of Monads. I won't claim to understand them, but here is a link along with usage of Maybe.

Happy coding.

梦幻的味道 2024-10-26 19:02:33

好的,现在换行到 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;
    }
}

控制台上打印了什么?

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

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

哦,购买接口的默认值是什么?哦,还有一个抽象类,当对抽象类中定义的默认值调用一个方法但调用另一个抽象方法时会发生什么? .... .... 为什么哦为什么让模型变得毫无意义,这又是多重继承问题!

一种解决方案是完全更改语法,以实现功能齐全的语法,其中空世界不存在,只有当您希望它们存在时才可能存在......但它不是一种类似 C 的语言和多范式.Net 的内容将会丢失。

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


哦,为了更好地确定答案回答你的问题:

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

Ok now wrap to the magic word of 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;
    }
}

What is printed on the console?

  • Nothing an exception about accessing an un-initialized object is thrown : Ok call this a NullReferenceException and that's the current world.
  • It doesn't build, the user needed to specify a value when declaring model, see last bullet point as it create the same result.
  • Some default for the model and some default for the thing : Ok before with null we at least had a way to know if an instance was correct now the compiler is generating strange look-alikes that don't contain anything but are still invalid as model objects...
  • Something defined by the type of the objects : Better as we could define a specific invalid state per object but now each object that could be invalid need to independently implement this along with a way to identify this state by the caller...

So basically for me it don't seem to solve anything to remove a null state, as the possibly invalid state still need to be managed anyway...

Oh buy the way what would be the default value of an interface ? Oh and an abstract class what would happen when a method is called on a default value that is defined in the abstract class but that call another method that is abstract ? .... .... Why oh why complicating the model for nothing, it's multiple-inheritance questions all over again !

One solution would be to change the syntax completely to go for a full functional one where the null world doesn't exits, only Maybes when you want them to exists... But it's not a C like language and the multi-paradigm-ness of .Net would be lost.

What might be missing is a null-propagating operator able to return null to model.Thing when model is null, like model.?.Thing


Oh and for good mesure an answer to your question :

  • The current class library evolved after the Microsoft-Java debacle and C# was build as a "better-Java" so changing the type system to remove null references would have been a big change. They already manage to introduce value types and removed manual boxing !
  • As the value type introduction show microsoft think a lot about speed... The fact that the the default for all types map to a zero fill is really important for fast array initialization for example. Otherwise initialization of arrays of reference values would have need special threatment.
  • Without null interop with C would not have been possible so at least at the MSIL level and in unsafe block they need to be allowed to survive.
  • Microsoft wanted to use the framework for VB6++ removing Nothing as it is called in VB would have radically changed the language, it already took years for users to switch from VB6 to VB.Net such a paradigm change might have been fatal for the language.
绿光 2024-10-26 19:02:33

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

但我想你的意思是:

为什么引用可以为 null ?

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

您还可以举出更多示例,但主要是 null 的存在是为了对“可选”属性/关系的概念进行建模。

Well, values (value-type vars) can only be null since the nullable types were introduced in Fx2.

But I suppose you mean:

Why can references be null ?

That is part of the usefulness of references. Consider a Tree or LinkedList, they would not be possible (unable to end) without null.

You could come up with many more examples, but mainly null exists to model the concept of 'optional' properties/relationships.

蓝咒 2024-10-26 19:02:33

歇斯底里的葡萄干。

这是 C 级语言的后遗症,在 C 级语言中,您依赖于显式的指针操作。现代声明性语言(Mercury、Haskell、OCaml 等)在没有 null 的情况下运行得非常愉快。在那里,每个值都必须显式构造。 “null”的想法是通过“option”类型来处理的,该类型有两个值:“no”(对应于 null)和“yes(x)”(对应于值为 x 的非 null)。您必须解压缩每个选项值才能决定要做什么,因此:没有空指针引用错误。

语言中没有空值可以让你省去很多麻烦,这种想法在高级语言中仍然存在,这确实是一种耻辱。

Hysterical raisins.

It's a hangover from C-level languages where you live on explicit pointer manipulation. Modern declarative languages (Mercury, Haskell, OCaml, etc.) get by quite happily without nulls. There, every value has to be explicitly constructed. The 'null' idea is handled through 'option' types, which have two values, 'no' (corresponding to null) and 'yes(x)' (corresponding to non-null with value x). You have to unpack each option value to decide what to do, hence: no null pointer reference errors.

Not having nulls in a language saves you so much grief, it really is a shame the idea still persists in high-level languages.

心清如水 2024-10-26 19:02:33

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

I'm not familiar with alternatives either, but I don't see a difference between Object.Empty and null, other than null lets you know that something is wrong when your code tries to access the object, wheras Object.Empty allows processing to continue. Sometimes you want one behavior, and sometimes you want the other. Distinguishing null from Empty is a useful tool for that.

一身软味 2024-10-26 19:02:33

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

现在,您可以通过定义可为 null 的类型为任何值类型指定 Null 值。

我认为我们不能总是为变量指定一个值,因为首先我们必须将其默认为某个值,这就出现了为什么特定值比其他值更具优势的问题。

To denote the nothingness concept, since 0 is not the right fit.

Now you can give any value type a Null value by defining a nullable type.

I think we can't have a value always for the variable because at first we have to default it to some value, and here comes the question why a specific value takes advantage over the others.

没有你我更好 2024-10-26 19:02:33

许多人可能无法理解没有空值的编码,如果 C# 没有空值,我怀疑它是否会流行到这样的程度。

话虽这么说,一个不错的选择是,如果您想允许可为空引用,则该引用必须显式可为空,就像值类型一样。

例如,

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

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

Many people probably can't wrap their head around coding without nulls, and if C# didn't have nulls, I doubt it would have caught on to the extent that it has.

That being said, a nice alternative would be, if you want to allow a nullable reference, then the reference would have to be explicitly nullable, like with value types.

For example,

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

Unfortunately, when C# 1.0 came out, there was no concept of Nullable; that was added in C# 2.0. Forcing references to have values would have broken the older programs.

一紙繁鸢 2024-10-26 19:02:33

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

如果没有 null

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

总之:

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

Tony Hoare 描述的 null 问题通常是由于以下事实造成的:在现代虚拟机出现之前,运行时系统没有像您这样干净地处理误用的 null 值有今天。滥用指针/引用确实仍然是一个问题,但是在使用 .NET 或 Java 时跟踪问题往往比以前在 C 中容易

null is just the name of the default value for a reference type. If null was not allowed, then the concept of "doesn't have a value" wouldn't go away, you would just represent it in a different way. In addition to having a special name, this default value also has special semantics in the event that it is misused - i.e. if you treat it like there is a value, when in fact there is not.

If there were no null:

  1. You would need to define sentinel default values for your types to use when "there is no value". As a trivial example, consider the last node of a forward singly-linked list.
  2. You would need to define semantics for operations performed on all of these sentinel values.
  3. The runtime would have additional overhead for handling the sentinel values. In modern virtual machines such as the ones used by .NET and Java, there is no overhead for null-checking prior to most function calls and dereferences because the CPU provides special ways to handle this case, and the CPU is optimized for cases where the references are non-null (e.g. branch prediction).

In summary:

  • The name would change, but the concept would not.
  • The burden of defining and communicating the default value and semantics for cases where you have "no value" is placed on developers and developer documentation.
  • Code executing on modern virtual machines would face code bloat and substantial performance degradation for many algorithms.

The problems with null described by Tony Hoare are typically due to the fact that prior to modern virtual machines, runtime systems did not have nearly as clean handling of misused null values like you have today. Misusing pointers/references does remain a problem, but tracking down the problem when working with .NET or Java tends to be much easier than it used to be in e.g. C.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文