不可为空的引用类型

Zif*_*fre 12 language-design non-nullable

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

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

Ada*_*ght 16

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

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

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

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

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


Bri*_*ian 11

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


Dan*_*ker 9

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

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

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

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

if (myObj)
{
    // in this scope, myObj is non-nullable, so can be used
}
Run Code Online (Sandbox Code Playgroud)

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

我再添加一点糖:

if (SomeMethodReturningANullable() into anotherObj)
{
    // anotherObj is non-nullable, so can be used
}
Run Code Online (Sandbox Code Playgroud)

这只是给出了anotherObj左边表达式结果的名称into,因此它可以在有效的范围内使用.

我会为?:运营商做同样的事情.

string message = GetMessage() into m ? m : "No message available"; 
Run Code Online (Sandbox Code Playgroud)

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

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

string message = GetMessage() or "No message available";
Run Code Online (Sandbox Code Playgroud)

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

(我还有一个内置的实例所有权概念;编译器会IDisposable.Dispose自动生成方法,~Destructor语法将用于扩充Dispose,就像在C++/CLI中一样.)

Spec#有另一个与非nullables相关的句法扩展,因为确保在构造期间正确初始化非nullables的问题:

class SpecSharpExampleClass
{
    private string! _nonNullableExampleField;

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

    }
}
Run Code Online (Sandbox Code Playgroud)

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