不可为 null 的引用类型
我正在设计一种语言,我想知道默认情况下使引用类型不可为空并使用“?”是否合理。 对于可为空值和引用类型。 这有什么问题吗? 对此你会怎么做:
class Foo {
Bar? b;
Bar b2;
Foo() {
b.DoSomething(); //valid, but will cause exception
b2.DoSomething(); //?
}
}
I'm designing a language, and I'm wondering if it's reasonable to make reference types non-nullable by default, and use "?" for nullable value and reference types. Are there any problems with this? What would you do about this:
class Foo {
Bar? b;
Bar b2;
Foo() {
b.DoSomething(); //valid, but will cause exception
b2.DoSomething(); //?
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我当前的语言设计理念是,可空性应该是程序员被迫要求的,而不是默认在引用类型上给出的(在这一点上,我同意 Tony Hoare - Google 在他最近的 QCon 演讲中的观点)。
在这个特定的示例中,对于不可空的 b2,它甚至无法通过静态检查:保守分析不能保证 b2 不为 NULL,因此该程序在语义上没有意义。
我的精神很简单。 引用是某些资源的间接句柄,我们可以遍历它来获取对该资源的访问权限。 可空引用要么是资源的间接句柄,要么是资源不可用的通知,并且人们永远无法预先确定正在使用哪种语义。 这要么预先进行大量检查(是否为空?不是?耶!),或者不可避免的 NPE(或同等情况)。 如今,大多数编程资源都没有受到大量资源限制或绑定到某些有限的基础模型 - 简单地说,空引用是......
当然,用更好的语言选择来解决 NULL 当前所满足的每种情况并不是一件小事,而且可能会增加更多的混乱。 我们总是可以使用不可变的资源,因此 NULL 在它唯一有用的状态(错误和漏洞)中并没有多大实际用途。 不过,必要的技术将继续存在,坦率地说,我很高兴——这使得在这个领域寻找更好的解决方案变得值得。
My current language design philosophy is that nullability should be something a programmer is forced to ask for, not given by default on reference types (in this, I agree with Tony Hoare - Google for his recent QCon talk).
On this specific example, with the unnullable b2, it wouldn't even pass static checks: Conservative analysis cannot guarantee that b2 isn't NULL, so the program is not semantically meaningful.
My ethos is simple enough. References are an indirection handle to some resource, which we can traverse to obtain access to that resource. Nullable references are either an indirection handle to a resource, or a notification that the resource is not available, and one is never sure up front which semantics are being used. This gives either a multitude of checks up front (Is it null? No? Yay!), or the inevitable NPE (or equivalent). Most programming resources are, these days, not massively resource constrained or bound to some finite underlying model - null references are, simplistically, one of...
Of course, solving each of the cases that NULL current caters for with a better linguistic choice is no small feat, and may add more confusion that it helps. We can always go to immutable resources, so NULL in it's only useful states (error, and hole) isn't much real use. Imperative technqiues are here to stay though, and I'm frankly glad - this makes the search for better solutions in this space worthwhile.
默认情况下使引用类型不可为空是唯一合理的选择。 我们被语言和运行时搞砸了; 你应该做正确的事。
Having reference types be non-nullable by default is the only reasonable choice. We are plagued by languages and runtimes that have screwed this up; you should do the Right Thing.
此功能位于 Spec# 中。 他们默认为可空引用并使用 ! 指示不可为空。 这是因为他们想要向后兼容。
在我梦想的语言中(我可能是唯一的用户!)我会做出与您相同的选择,默认情况下不可为空。
我还会将使用 . 可空引用上的运算符(或任何其他会取消引用它的东西)。 你会如何使用它们? 您必须首先将它们转换为不可空值。 你会怎么做? 通过测试它们是否为空。
在 Java 和 C# 中,
if
语句只能接受bool
测试表达式。 我将其扩展为接受可为 null 的引用变量的名称:这种特殊语法对于 C/C++ 程序员来说并不奇怪。 我更喜欢像这样的特殊语法,以清楚地表明我们正在执行一项检查,以修改真值分支中名称
myObj
的类型。我想再添加一点糖:
这只是将名称
anotherObj
赋予into
左侧表达式的结果,因此它可以在其有效范围。我会对
?:
运算符做同样的事情。请注意,
string message
不可为 null,但上述测试的两个可能结果也是不可为 null,因此赋值为 value。然后,对于将值替换为 null 的常见情况,可能需要一些糖分:
显然
or
只能有效地应用于左侧的可空类型和右侧的不可空类型边。(我还有实例字段的内置所有权概念;编译器会自动生成
IDisposable.Dispose
方法,并且将使用~Destructor
语法增强Dispose
,就像在 C++/CLI 中一样。)是由于确保非空值在构造过程中正确初始化的问题:
Spec# 有另一个与非空值相关的语法扩展,这 换句话说,您必须以与使用
base
或this
调用其他构造函数相同的方式初始化字段 - 当然,除非您直接在字段声明旁边初始化它们。This feature was in Spec#. They defaulted to nullable references and used ! to indicate non-nullables. This was because they wanted backward compatibility.
In my dream language (of which I'd probably be the only user!) I'd make the same choice as you, non-nullable by default.
I would also make it illegal to use the . operator on a nullable reference (or anything else that would dereference it). How would you use them? You'd have to convert them to non-nullables first. How would you do this? By testing them for null.
In Java and C#, the
if
statement can only accept abool
test expression. I'd extend it to accept the name of a nullable reference variable:This special syntax would be unsurprising to C/C++ programmers. I'd prefer a special syntax like this to make it clear that we are doing a check that modifies the type of the name
myObj
within the truth-branch.I'd add a further bit of sugar:
This just gives the name
anotherObj
to the result of the expression on the left of theinto
, so it can be used in the scope where it is valid.I'd do the same kind of thing for the
?:
operator.Note that
string message
is non-nullable, but so are the two possible results of the test above, so the assignment is value.And then maybe a bit of sugar for the presumably common case of substituting a value for null:
Obviously
or
would only be validly applied to a nullable type on the left side, and a non-nullable on the right side.(I'd also have a built-in notion of ownership for instance fields; the compiler would generate the
IDisposable.Dispose
method automatically, and the~Destructor
syntax would be used to augmentDispose
, exactly as in C++/CLI.)Spec# had another syntactic extension related to non-nullables, due to the problem of ensuring that non-nullables had been initialized correctly during construction:
In other words, you have to initialize fields in the same way as you'd call other constructors with
base
orthis
- unless of course you initialize them directly next to the field declaration.查看 Elvis 操作员提案对于 Java 7。这做了类似的事情,因为它将 null 检查和方法分派封装在一个运算符中,如果对象为 null,则使用指定的返回值。 因此:
检查 String s 是否为 null,如果是则返回字符串“null”,如果不是则返回字符串的值。 也许值得深思。
Have a look at the Elvis operator proposal for Java 7. This does something similar, in that it encapsulates a null check and method dispatch in one operator, with a specified return value if the object is null. Hence:
checks if the String s is null, and returns the string "null" if so, and the value of the string if not. Food for thought, perhaps.
其他语言中类似功能的几个示例:
还有
Nullable
(来自 C#)但这不是一个很好的例子,因为引用类型与值类型的处理方式不同。在您的示例中,您可以添加条件消息发送运算符,例如
仅当消息非空时才将消息发送到
b
。A couple of examples of similar features in other languages:
There's also
Nullable<T>
(from C#) but that is not such a good example because of the different treatment of reference vs. value types.In your example you could add a conditional message send operator, e.g.
To send a message to
b
only if it is non-null.让可空性成为一个配置设置,可以在作者的源代码中强制执行。 这样,您将允许那些默认情况下喜欢可空对象的人在源代码中享受它们,同时允许那些希望所有对象默认情况下不可为空的人也能拥有这些。 此外,提供关键字或其他工具来显式标记对象和类型的哪些声明可以为空,哪些不能(例如
nullable
和not-nullable
)来覆盖全局默认值。例如,
许多人认为给每个人他们想要的东西是不可能的,但如果你正在设计一种新的语言,请尝试新的东西。 Tony Hoare 在 1965 年引入了
null
的概念,因为他无法抗拒(他自己的话),从那时起我们就为此付出了代价(而且,他自己的话,这个人对此感到遗憾)。 重点是,聪明、有经验的人会犯一些让我们其他人付出代价的错误,不要把本页上任何人的建议当作唯一的真理,包括我的。 评估并思考一下。我读过很多关于我们这些缺乏经验的程序员的抱怨,他们真的不知道在哪里真正使用
null
,在哪里不使用,向我们展示了模式和反模式,旨在防止我们在脚。 一直以来,数百万仍然缺乏经验的程序员用允许null
的语言编写了更多代码。 我可能缺乏经验,但我知道我的哪些对象不会从可为空中受益。Have the nullability be a configuration setting, enforceable in the authors source code. That way, you will allow people who like nullable objects by default enjoy them in their source code, while allowing those who would like all their objects be non-nullable by default have exactly that. Additionally, provide keywords or other facility to explicitly mark which of your declarations of objects and types can be nullable and which cannot, with something like
nullable
andnot-nullable
, to override the global defaults.For instance
Many people argue that giving everyone what they want is impossible, but if you are designing a new language, try new things. Tony Hoare introduced the concept of
null
in 1965 because he could not resist (his own words), and we are paying for it ever since (also, his own words, the man is regretful of it). Point is, smart, experienced people make mistakes that cost the rest of us, don't take anyones advice on this page as if it were the only truth, including mine. Evaluate and think about it.I've read many many rants on how it's us poor inexperienced programmers who really don't understand where to really use
null
and where not, showing us patterns and antipatterns that are meant to prevent shooting ourselves in the foot. All the while, millions of still inexperienced programmers produce more code in languages that allownull
. I may be inexperienced, but I know which of my objects don't benefit from being nullable.13 年后,C# 做到了 。
是的,这是自 Barbara 和 Stephen 1974 年发明类型以来语言的最大改进。 :
Here we are, 13 years later, and C# did it.
And, yes, this is the biggest improvement in languages since Barbara and Stephen invented types in 1974.:
我认为空值很好:它们清楚地表明您做错了什么。 如果您未能在某处初始化引用,您将立即收到通知。
另一种方法是有时将值初始化为默认值。 逻辑错误就更难以检测,除非您将检测逻辑放入这些默认值中。 这与刚刚获得空指针异常相同。
I think null values are good: They are a clear indication that you did something wrong. If you fail to initialize a reference somewhere, you'll get an immediate notice.
The alternative would be that values are sometimes initialized to a default value. Logical errors are then a lot more difficult to detect, unless you put detection logic in those default values. This would be the same as just getting a null pointer exception.