不可为 null 的引用类型

发布于 2024-07-16 04:17:32 字数 251 浏览 9 评论 0原文

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

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 技术交流群。

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

发布评论

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

评论(8

手心的温暖 2024-07-23 04:17:32

我当前的语言设计理念是,可空性应该是程序员被迫要求的,而不是默认在引用类型上给出的(在这一点上,我同意 Tony Hoare - Google 在他最近的 QCon 演讲中的观点)。

在这个特定的示例中,对于不可空的 b2,它甚至无法通过静态检查:保守分析不能保证 b2 不为 NULL,因此该程序在语义上没有意义。

我的精神很简单。 引用是某些资源的间接句柄,我们可以遍历它来获取对该资源的访问权限。 可空引用要么是资源的间接句柄,要么是资源不可用的通知,并且人们永远无法预先确定正在使用哪种语义。 这要么预先进行大量检查(是否为空?不是?耶!),或者不可避免的 NPE(或同等情况)。 如今,大多数编程资源都没有受到大量资源限制或绑定到某些有限的基础模型 - 简单地说,空引用是......

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

当然,用更好的语言选择来解决 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...

  • Laziness: "I'll just bung a null in here". Which frankly, I don't have too much sympathy with
  • Confusion: "I don't know what to put in here yet". Typically also a legacy of older languages, where you had to declare your resource names before you knew what your resources were.
  • Errors: "It went wrong, here's a NULL". Better error reporting mechanisms are thus essential in a language
  • A hole: "I know I'll have something soon, give me a placeholder". This has more merit, and we can think of ways to combat this.

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.

天生の放荡 2024-07-23 04:17:32

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

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.

£冰雨忧蓝° 2024-07-23 04:17:32

此功能位于 Spec# 中。 他们默认为可空引用并使用 ! 指示不可为空。 这是因为他们想要向后兼容。

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

我还会将使用 . 可空引用上的运算符(或任何其他会取消引用它的东西)。 你会如何使用它们? 您必须首先将它们转换为不可空值。 你会怎么做? 通过测试它们是否为空。

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

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,但上述测试的两个可能结果也是不可为 null,因此赋值为 value。

然后,对于将值替换为 null 的常见情况,可能需要一些糖分:

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

显然 or 只能有效地应用于左侧的可空类型和右侧的不可空类型边。

(我还有实例字段的内置所有权概念;编译器会自动生成 IDisposable.Dispose 方法,并且将使用 ~Destructor 语法增强 Dispose,就像在 C++/CLI 中一样。)

是由于确保非空值在构造过程中正确初始化的问题:

class SpecSharpExampleClass
{
    private string! _nonNullableExampleField;

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

    }
}

Spec# 有另一个与非空值相关的语法扩展,这 换句话说,您必须以与使用 basethis 调用其他构造函数相同的方式初始化字段 - 当然,除非您直接在字段声明旁边初始化它们。

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 a bool test expression. I'd extend it to accept the name of a nullable reference variable:

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

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:

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

This just gives the name anotherObj to the result of the expression on the left of the into, so it can be used in the scope where it is valid.

I'd do the same kind of thing for the ?: operator.

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

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:

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

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 augment Dispose, 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:

class SpecSharpExampleClass
{
    private string! _nonNullableExampleField;

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

    }
}

In other words, you have to initialize fields in the same way as you'd call other constructors with base or this - unless of course you initialize them directly next to the field declaration.

暮色兮凉城 2024-07-23 04:17:32

查看 Elvis 操作员提案对于 Java 7。这做了类似的事情,因为它将 null 检查和方法分派封装在一个运算符中,如果对象为 null,则使用指定的返回值。 因此:

String s = mayBeNull?.toString() ?: "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:

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

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.

回心转意 2024-07-23 04:17:32

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

还有Nullable (来自 C#)但这不是一个很好的例子,因为引用类型与值类型的处理方式不同。

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

b?->DoSomething();

仅当消息非空时才将消息发送到 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.

b?->DoSomething();

To send a message to b only if it is non-null.

紫轩蝶泪 2024-07-23 04:17:32

让可空性成为一个配置设置,可以在作者的源代码中强制执行。 这样,您将允许那些默认情况下喜欢可空对象的人在源代码中享受它们,同时允许那些希望所有对象默认情况下不可为空的人也能拥有这些。 此外,提供关键字或其他工具来显式标记对象和类型的哪些声明可以为空,哪些不能(例如 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 的语言编写了更多代码。 我可能缺乏经验,但我知道我的哪些对象不会从可为空中受益。

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 and not-nullable, to override the global defaults.

For instance

/// "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.

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 allow null. I may be inexperienced, but I know which of my objects don't benefit from being nullable.

当爱已成负担 2024-07-23 04:17:32

13 年后,C# 做到了

是的,这是自 Barbara 和 Stephen 1974 年发明类型以来语言的最大改进。

使用抽象数据类型进行编程

芭芭拉·利斯科夫
麻省理工学院
MAC项目
马萨诸塞州剑桥

史蒂芬·齐勒斯
剑桥系统集团
IBM 系统开发部
马萨诸塞州剑桥

摘要

动机
非常高级语言的工作背后是为了简化
通过为程序员提供一种语言来完成编程任务
包含适合他的问题领域的原语或抽象。
然后程序员就可以把精力花在正确的地方; 他
集中精力解决他的问题,最终的程序将是
结果更加可靠。 显然,这是一个值得实现的目标。
不幸的是,设计师很难选择
推进语言使用者可能的所有抽象概念
需要。 如果要使用一种语言,它很可能会被用来
解决其设计者没有预见到的问题
嵌入语言中的抽象是不够的。 这张纸
提出了一种方法,允许一组内置抽象
当发现需要新的数据抽象时,会进行扩充。
这种处理抽象的方法是工作的产物
设计一种用于结构化编程的语言。 相关方面
描述了这种语言的功能,并提供了使用和使用的示例
给出了抽象的定义。

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.:

Programming With Abstract Data Types

Barbara Liskov
Massachusetts Institute of Technology
Project MAC
Cambridge, Massachusetts

Stephen Zilles
Cambridge Systems Group
IBM Systems Development Division
Cambridge, Massachusetts

Abstract

The motivation
behind the work in very-high-level languages is to ease the
programming task by providing the programmer with a language
containing primitives or abstractions suitable to his problem area.
The programmer is then able to spend his effort in the right place; he
concentrates on solving his problem, and the resulting program will be
more reliable as a result. Clearly, this is a worthwhile goal.
Unfortunately, it is very difficult for a designer to select in
advance all the abstractions which the users of his language might
need. If a language is to be used at all, it is likely to be used to
solve problems which its designer did not envision, and for which the
abstractions embedded in the language are not sufficient. This paper
presents an approach which allows the set of built-in abstractions to
be augmented when the need for a new data abstraction is discovered.
This approach to the handling of abstraction is an outgrowth of work
on designing a language for structured programming. Relevant aspects
of this language are described, and examples of the use and
definitions of abstractions are given.

归途 2024-07-23 04:17:32

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

另一种方法是有时将值初始化为默认值。 逻辑错误就更难以检测,除非您将检测逻辑放入这些默认值中。 这与刚刚获得空指针异常相同。

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.

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