不可为空的引用类型

时间:2009-03-28 19:09:51

标签: language-design non-nullable

我正在设计一种语言,我想知道默认情况下使引用类型不可为空是否合理,并使用“?”可以为空的值和引用类型。这有什么问题吗?你会怎么做:

class Foo {
    Bar? b;
    Bar b2;
    Foo() {
        b.DoSomething(); //valid, but will cause exception
        b2.DoSomething(); //?
    }
}

7 个答案:

答案 0 :(得分:16)

我目前的语言设计理念是,可空性应该是程序员被迫要求的东西,而不是默认情况下参考类型(在此,我同意Tony Hoare - Google最近的QCon演讲)。

在这个具体的例子中,使用unnullable b2,它甚至不会通过静态检查:保守分析不能保证b2不是NULL,所以程序在语义上没有意义。

我的精神很简单。引用是某些资源的间接句柄,我们可以遍历该句柄以获取对该资源的访问权限。可空引用是 资源的间接句柄,资源不可用的通知,并且一个人永远不能确定正在使用哪种语义。这预先给出了大量的检查(是空的吗?不是吗?是的!),或者不可避免的NPE(或等价物)。如今,大多数编程资源都不是大规模资源约束或绑定到某些有限的底层模型 - 空引用简单地说就是......

  • 懒惰:“我只会在这里打空”。
  • 坦率地说,我没有太多的同情
  • 困惑:“我不知道该把什么放在这里”。通常也是旧语言的遗留物,您必须在知道资源之前声明资源名称。
  • 错误:“它出了问题,这里是一个NULL”。因此,更好的错误报告机制在语言中是必不可少的
  • 一个洞:“我知道我很快会有东西,给我一个占位符”。这有更多的优点,我们可以想办法解决这个问题。

当然,通过更好的语言选择来解决NULL当前迎合的每种情况都是不小的壮举,并且可能会增加它有助于增加的混乱。我们总是可以去不可变资源,所以NULL中唯一有用的状态(错误和漏洞)并没有太多实际用途。尽管如此,势在必行的技术仍然存在,我很高兴 - 这使得在这个领域寻找更好的解决方案是值得的。

答案 1 :(得分:11)

默认情况下,引用类型不可为空是唯一合理的选择。我们被困扰它的语言和运行时所困扰;你应该做正确的事。

答案 2 :(得分:9)

此功能位于Spec#。他们违反了可以引用的引用并使用了!表示非空的。这是因为他们想要向后兼容。

用我梦寐以求的语言(其中我可能是唯一的用户!)我会做出与你相同的选择,默认情况下不可为空。

我也会认为使用它是违法的。可操作引用的操作符(或任何其他可以取消引用它的操作符)。你会怎么用它们?你必须先将它们转换为非空格。你会怎么做?通过测试它们为null。

在Java和C#中,if语句只能接受bool测试表达式。我将它扩展为接受可以为空的引用变量的名称:

if (myObj)
{
    // in this scope, myObj is non-nullable, so can be used
}

对于C / C ++程序员来说,这种特殊的语法并不令人惊讶。我更喜欢这样的特殊语法,以明确我们正在进行检查,修改真值分支中名称myObj的类型。

我要加点糖:

if (SomeMethodReturningANullable() into anotherObj)
{
    // anotherObj is non-nullable, so can be used
}

这只是给anotherObj左侧表达式的结果命名into,因此它可以在有效的范围内使用。

我会为?:运算符做同样的事情。

string message = GetMessage() into m ? m : "No message available"; 

请注意,string message不可为空,但上述测试的两个可能结果也是如此,因此赋值是值。

然后可能有点糖,因为可能是将值替换为null的常见情况:

string message = GetMessage() or "No message available";

显然,or只能在左侧有效地应用于可空类型,而在右侧有效应用于不可为空。

(我还有一个内置的实例所有权概念;编译器会自动生成IDisposable.Dispose方法,~Destructor语法将用于增强Dispose完全和C ++ / CLI一样。)

Spec#有另一个与非nullables有关的语法扩展,因为确保在构造过程中正确初始化了非nullables的问题:

class SpecSharpExampleClass
{
    private string! _nonNullableExampleField;

    public SpecSharpExampleClass(string s)
        : _nonNullableExampleField(s) 
    {

    }
}

换句话说,您必须使用与使用basethis调用其他构造函数相同的方式初始化字段 - 除非您在字段声明旁边直接初始化它们。 / p>

答案 3 :(得分:4)

查看Java 7的Elvis operator提议。这类似于它,它在一个运算符中封装了一个空检查和方法分派,如果对象为null,则返回指定的返回值。因此:

String s = mayBeNull?.toString() ?: "null";

检查String s是否为null,如果是,则返回字符串“null”,否则返回字符串的值。或许,值得深思。

答案 4 :(得分:3)

其他语言中有类似功能的几个例子:

还有Nullable<T>(来自C#),但由于对参考与价值类型的不同处理,这不是一个很好的例子。

在您的示例中,您可以添加条件消息发送操作符,例如

b?->DoSomething();

仅当消息为非空时才向b发送消息。

答案 5 :(得分:-1)

将可为空性作为配置设置,在作者源代码中强制执行。这样,您将允许那些喜欢可以为空的对象的人默认在源代码中享受它们,同时允许那些希望所有对象在默认情况下都不可为空的人具有这一点。此外,提供关键字或其他工具以明确标记哪些对象和类型的声明可以为空,哪些不能(如nullablenot-nullable)覆盖全局默认值。

例如

/// "translation unit 1"

#set nullable
{ /// Scope of default override, making all declarations within the scope nullable implicitly
     Bar bar; /// Can be null
     non-null Foo foo; /// Overriden, cannot be null
     nullable FooBar foobar; /// Overriden, can be null, even without the scope definition above 
}

/// Same style for opposite

/// ...

/// Top-bottom, until reset by scoped-setting or simply reset to another value
#set nullable;

/// Nullable types implicitly

#clear nullable;

/// Can also use '#set nullable = false' or '#set not-nullable = true'. Ugly, but human mind is a very original, mhm, thing.

许多人认为给每个人提供他们想要的东西是不可能的,但如果你正在设计一种新语言,那就试试新事物吧。 Tony Hoare在1965年介绍了null的概念,因为他无法抗拒(他自己的话),而且我们从此付出了代价(同样,他自己的话,男人也为此感到遗憾)。重点是,聪明,经验丰富的人会犯错,导致我们其他人付出代价,不要在这个页面上接受任何建议,好像这是唯一的事实,包括我的。评估并思考它。

我读过很多关于我们这些贫穷的经验不足的程序员如何真正不知道在哪里真正使用null而在哪里没有,我们看到的模式和反模式是为了防止我们在脚丫子。一直以来,数百万仍然缺乏经验的程序员用允许null的语言生成更多代码。我可能缺乏经验,但我知道哪些对象不会因为可以为空而受益。

答案 6 :(得分:-3)

我认为空值很好:它们清楚地表明你做错了什么。如果您未能在某处初始化参考,您将立即得到通知。

替代方案是值有时会初始化为默认值。除非您将检测逻辑放在这些默认值中,否则逻辑错误将更难以检测。这与获取空指针异常相同。