为什么我们没有两个空值?
我经常想知道为什么带有 null
代表“无值” 的语言不区分被动“我不知道值是什么” 和更自信的“没有价值。”。
在某些情况下,我希望区分两者(特别是在处理用户输入和数据库时)。
我想象如下,我们将两个状态命名为 unknown
和 null
:
var apple;
while (apple is unknown)
{
askForApple();
}
if (apple is null)
{
sulk();
}
else
{
eatApple(apple);
}
显然,我们可以通过在其他地方手动存储状态来摆脱它,但我们也可以对空值执行此操作。
那么,如果我们可以有一个 null
,为什么不能有两个呢?
I've often wondered why languages with a null
representing "no value" don't differentiate between the passive "I don't know what the value is" and the more assertive "There is no value.".
There have been several cases where I'd have liked to differentiate between the two (especially when working with user-input and databases).
I imagine the following, where we name the two states unknown
and null
:
var apple;
while (apple is unknown)
{
askForApple();
}
if (apple is null)
{
sulk();
}
else
{
eatApple(apple);
}
Obviously, we can get away without it by manually storing the state somwhere else, but we can do that for nulls too.
So, if we can have one null
, why can't we have two?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(27)
一个为空还不够糟糕吗?
Isn't is bad enough that we have one null?
在我的编程中,我最近采用了区分“语言为空”和“领域为空”的做法。
“语言空”是编程语言提供的特殊值,用于表示变量“没有值”。 它需要作为数据结构、参数列表和返回值中的虚拟值。
“domain null”是实现 NullObject 设计模式的任意数量的对象。 实际上,每个域上下文都有一个不同的域 null。
程序员使用 null 语言作为包罗万象的域 null 是相当常见的,但我发现它往往会使代码更加过程化(不太面向对象)并且意图更难以辨别。
每次想要一个空值时,问问自己:这是语言空值,还是领域空值?
In my programming, I recently adopted the practice of differentiating "language null" and "domain null".
The "language null" is the special value that is provided by the programming language to express that a variable has "no value". It is needed as dummy value in data structures, parameter lists, and return values.
The "domain null" is any number of objects that implement the NullObject design pattern. Actually, you have one distinct domain null for each domain context.
It is fairly common for programmers to use the language null as a catch-all domain null, but I have found that it tends to make code more procedural (less object oriented) and the intent harder to discern.
Every time to want a null, ask yourself: is that a language null, or a domain null?
在大多数编程语言中,null 表示“空”或“未定义”。 另一方面,“未知”是不同的。 本质上,“未知”描述了对象的状态。 这个状态一定来自你的程序中的某个地方。
查看空对象模式。 它可能会帮助您实现您想要实现的目标。
In most programming languages null means "empty" or "undefined". "Unknown" on the other hand is something different. In essence "unknown" describes the state of the object. This state must have come from somewhere in your program.
Have a look at the Null Object pattern. It may help you with what you are trying to achieve.
javascript实际上同时具有null和undefined(http://www.w3schools.com/jsref/jsref_undefined。 asp),但许多其他语言没有。
javascript actually has both null and undefined (http://www.w3schools.com/jsref/jsref_undefined.asp), but many other languages don't.
在极少数情况下,当您需要这样的东西时,创建一个指示未知的静态常量是很容易的。
It would be easy enough to create a static constant indicating unknown, for the rare cases when you'd need such a thing.
值的存在:
vars().has_key('variableName')
isset(variable)
typeof(variable) != 'undefined'
(variable != undef)
或者如果你愿意的话:(已定义的变量)
当然,当变量未定义时,它不是 NULL
Existence of value:
vars().has_key('variableName')
isset(variable)
typeof(variable) != 'undefined'
(variable != undef)
or if you wish:(defined variable)
Of course, when variable is undefined, it's not NULL
Null 类型是所有引用类型的子类型 - 您可以使用 null 代替对任何类型对象的引用 - 这严重削弱了类型系统。 它被认为是历史上的坏主意之一它的创建者,并且只存在于检查地址是否为零很容易实现。
The Null type is a subtype of all reference types - you can use null instead of a reference to any type of object - which severely weakens the type system. It is considered one of the a historically bad idea by its creator, and only exists as checking whether an address is zero is easy to implement.
为什么两点停?
当我在大学学习数据库时,我们被告知有人(抱歉,不记得研究人员或论文的名字)查看了一堆数据库模式,发现 null 有大约 17 种不同的含义:“不要还知道”、“无法知道”、“不适用”、“无”、“空”、“未采取行动”、“未使用字段”等等。
Why stop at two?
When I took databases in college, we were told that somebody (sorry, don't remember the name of the researcher or paper) had looked at a bunch of db schemas and found that null had something like 17 different meanings: "don't know yet", "can't be known", "doesn't apply", "none", "empty", "action not taken", "field not used", and so on.
在 haskell 中,你可以定义这样的东西:
这个想法是,未知值保存一些
b
类型的数据,可以将它们转换为已知值(你如何做到这一点取决于具体类型a
和b
)。细心的读者会注意到,我只是将 Maybe 和 Either 组合成一种数据类型:)
In haskell you can define something like this:
The idea being that Unknown values hold some data of type
b
that can transform them into known values (how you'd do that depends on the concrete typesa
andb
).The observant reader will note that I'm just combining Maybe and Either into one data type :)
至于为什么我们没有两个 null,是不是因为历史上在 C 中,NULL 是一个简单的
#define
而不是语言根本吗?As to why we don't have two nulls, is it down to the fact that, historically in C, NULL was a simple
#define
and not a distinct part of the language at all?在 PHP Strict 中,您需要对设置变量进行
isset()
检查(否则会抛出警告)Within PHP Strict you need to do an
isset()
check for set variables (or else it throws a warning)在 .net 语言中,您可以使用可空类型来解决此问题值类型的问题。
然而,对于引用类型来说,问题仍然存在。 由于 .net 中不存在指针之类的东西(至少在“安全”块中),因此“object?o”将无法编译。
In .net langages, you can use nullable types, which address this problem for value types.
The problem remains, however, for reference types. Since there's no such thing as pointers in .net (at least in 'safe' blocks), "object? o" won't compile.
注意
null
是可接受的但已知的条件。 在我看来,未知状态是另一回事。 我在顶帖评论部分与丹的对话将澄清我的立场。 谢谢丹!您可能想要查询的是该对象是否已初始化。
Actionscript 有这样的东西(
null
和undefined
)。 但有一些限制。请参阅文档:
Note
null
is an acceptable, yet known condition. An unknown state is a different thing IMO. My conversation with Dan in the comments' section of the top post will clarify my position. Thanks Dan!.What you probably want to query is whether the object was initialized or not.
Actionscript has such a thing (
null
andundefined
). With some restrictions however.See documentation:
有些人会认为我们应该摆脱
null
完全,这似乎相当有效。 毕竟,为什么要停在两个空值处呢? 为什么不是三个或四个等等,每个代表一个“无值”状态?想象一下,
refused
、null
、invalid
:Some people will argue that we should be rid of
null
altogether, which seems fairly valid. After all, why stop at two nulls? Why not three or four and so on, each representing a "no value" state?Imagine this, with
refused
,null
,invalid
:已经尝试过了:Visual Basic 6 有 Nothing、Null 和 Empty。 它导致了如此糟糕的代码,在传奇的讨厌 VB 的十三种方法 多布斯博士的文章。
正如其他人所建议的那样,请使用 空对象模式。
It's been tried: Visual Basic 6 had Nothing, Null, and Empty. And it led to such poor code, it featured at #12 in the legendary Thirteen Ways to Loathe VB article in Dr Dobbs.
Use the Null Object pattern instead, as others have suggested.
问题在于,在强类型语言中,这些额外的空值应该保存有关类型的特定信息。
基本上,您的额外 null 是某种元信息,可以取决于类型的元信息。
某些值类型具有此额外信息,例如许多数字类型具有 NaN 常量。
在动态类型语言中,您必须考虑没有值 (null) 的引用与类型可以是任何内容(未知或未定义)的变量之间的差异,
例如,在静态类型 C# 中,类型为
String
可以为 null,因为它是引用类型。Int32
类型的变量不能,因为它是值类型,所以不能为 null。 我们总是知道类型。在动态类型的 Javascript 中,变量的类型可以保持未定义,在这种情况下,需要区分空引用和未定义的值。
The problem is that in a strongly typed language these extra nulls are expected to hold specific information about the type.
Basically your extra null is meta-information of a sort, meta-information that can depend on type.
Some value types have this extra information, for instance many numeric types have a NaN constant.
In a dynamically typed language you have to account for the difference between a reference without a value (null) and a variable where the type could be anything (unknown or undefined)
So, for instance, in statically typed C# a variable of type
String
can be null, because it is a reference type. A variable of typeInt32
cannot, because it is a value type it cannot be null. We always know the type.In dynamically typed Javascript a variable's type can be left undefined, in which case the distinction between a null reference and an undefined value is needed.
有些人已经比你领先一步了。 ;)
Some people are one step ahead of you already. ;)
考虑到西方哲学花了很长时间才弄清楚如何谈论“无”的概念……是的,我对这种区别一度被忽视并不感到太惊讶。
Given how long it took Western philosophy to figure out how it was possible to talk about the concept of "nothing"... Yeah, I'm not too surprised the distinction got overlooked for a while.
我认为有一个 NULL 是处理基本模式的一个较低公分母。
在“做其他事情”部分中,有很多种可能性,从“好吧,算了”到尝试在其他地方获取“东西”。 如果您不简单地忽略 NULL 的东西,您可能需要知道为什么“thing”是 NULL。 拥有多种类型的 NULL,将帮助您回答该问题,但可能的答案很多,如此处其他答案所暗示的那样。 丢失的东西可能只是一个错误,可能是尝试获取它时的错误,它现在可能不可用,等等。 要决定哪些情况适用于您的代码(这意味着您必须处理它们),它是特定于领域的。 因此,最好使用应用程序定义的机制来编码这些原因,而不是寻找尝试处理所有原因的语言功能。
I think having one NULL is a lower-common denominator to deal with the basic pattern
In the "do something else" part, there are a wide range of possibilities from "okay, forget it" to trying to get "thing" somewhere else. If you don't simply ignore something that's NULL, you probably need to know why "thing" was NULL. Having multiple types of NULL, would help you answering that question, but the possible answers are numerous as hinted in the other answers here. The missing thing could be simply a bug, it could be an error when trying to get it, it may not be available right now, and so on. To decide which cases apply to your code -- which means you have to handle them -- it domain specific. So it's better to use an application defined mechanism to encode these reasons instead of finding a language feature that tries to deal with all of them.
这是因为 Null 是您所使用的语言的产物,而不是程序员的便利。 它描述了对象在使用它的上下文中自然发生的状态。
It's because Null is an artifact of the language you're using, not a programmer convenience. It describes a naturally occurring state of the object in the context in which it is being used.
如果您使用 .NET 3.0+ 并且需要其他东西,您可以尝试 也许是 Monad。 您可以创建所需的任何“也许”类型,并使用 LINQ 语法进行相应的处理。
If you are using .NET 3.0+ and need something else, you might try the Maybe Monad. You could create whatever "Maybe" types you need and, using LINQ syntax, process accordingly.
首先检查是否有苹果信息,然后检查是否有苹果。 您不需要为此提供不同的母语支持,只需使用正确的类即可。
First you check if you have the apple info, later you check if there is an apple or not. You don't need different native language support for that, just use the right classes.
对我来说,null 代表缺乏价值,我尝试仅用它来代表这一点。 当然,您可以为 null 赋予任意多种含义,就像您可以使用 0 或 -1 来表示错误而不是其数值一样。 不过,为一种表示赋予不同的含义可能会含糊不清,所以我不推荐它。
您的示例可以像 apple.isRefused() 或 !apple.isValid() 一样编码,只需很少的工作; 无论如何,你应该事先定义什么是无效的苹果,所以我看不到拥有更多关键字的好处。
For me null represents lack of value, and I try to use it only to represent that. Of course you can give null as many meanings as you like, just like you can use 0 or -1 to represent errors instead of their numerical values. Giving various meanings to one representation could be ambiguous though, so I wouldn't recommend it.
Your examples can be coded like apple.isRefused() or !apple.isValid() with little work; you should define beforehand what is an invalid apple anyway, so I don't see the gain of having more keywords.
您始终可以创建一个对象并将其分配给同一个静态字段以获得第二个空值。
例如,这用于允许元素为空的集合中。 在内部,它们使用
private static final Object UNSET = new Object
用作未设置的值,因此允许您在集合中存储null
。 (我记得,Java 的集合框架将此对象称为 TOMBSTONE,而不是 UNSET。或者这是 Smalltalk 的集合框架?)You can always create an object and assign it to same static field to get a 2nd null.
For example, this is used in collections that allow elements to be null. Internally they use a
private static final Object UNSET = new Object
which is used as unset value and thus allows you to storenull
s in the collection. (As I recall, Java's collection framework calls this object TOMBSTONE instead of UNSET. Or was this Smalltalk's collection framework?)VB6
VB6
两个空值将是最错误的答案。 如果一个空值还不够,则需要无穷多个空值。
Null 可能意味着:
并且您的特定域可能需要许多其他类型的“带外”值。 实际上,这些值在域中,并且在每种情况下都需要具有明确定义的含义。 (因此,无穷大实际上为零)
Two nulls would be the wrongest answer around. If one null is not enough, you need infinity nulls.
Null Could mean:
And your particular domain may need many other kinds of 'out of band' values. Really, these values are in the domain, and need to have a well defined meaning in each case. (ergo, infinity really is zero)