当空引用似乎不可能时,为什么我们会收到可能取消引用空引用警告? [英] Why do we get possible dereference null reference warning, when null reference does not seem to be possible?

查看:87
本文介绍了当空引用似乎不可能时,为什么我们会收到可能取消引用空引用警告?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 HNQ 上阅读了这个问题后,我继续阅读了关于 C# 8 中的可空引用类型,并做了一些实验.

我非常清楚,当有人说我发现了一个编译器错误!"时,10 次中有 9 次,甚至更频繁.这其实是有意为之,也是自己的误会.而且由于我今天才开始研究这个功能,很明显我对它不是很了解.有了这个,让我们看看这段代码:

#nullable enable课程计划{静态无效主(){var s = "";var b = s == null;//如果你把这行注释掉,下一行的警告就会消失var i = s.Length;//警告 CS8602:取消引用可能为空的引用}}

阅读我上面链接的文档后,我希望 s == null 行给我一个警告——毕竟 s 显然是不可为空的,所以将它与 null 进行比较是没有意义的.

相反,我在 next 行收到警告,警告说 s 可能是空引用,尽管对于人类来说,它是显然不是.

此外,如果我们不将 snull 进行比较,则不会显示警告.

我在谷歌上搜索了一些一个 GitHub 问题,其中结果完全是关于其他事情,但在此过程中,我与一位贡献者进行了对话,他对这种行为提供了更多见解(例如 空检查通常是告诉编译器重置其先前推理的有用方法关于变量的可空性.").然而,这仍然给我留下了悬而未决的主要问题.

与其创建一个新的 GitHub 问题,并可能占用非常忙碌的项目贡献者的时间,不如将其发布给社区.

你能解释一下这是怎么回事吗?为什么?特别是,为什么在 s == null 行上没有生成警告,为什么我们有 CS8602 而它看起来不像 null> 这里可以参考吗?如果可空性推断不是防弹的,如链接的 GitHub 线程所暗示的那样,它怎么会出错?有哪些例子?

解决方案

这实际上是@stuartd 链接的答案的重复,因此我不打算在此处详细介绍.但问题的根源在于,这既不是语言错误也不是编译器错误,而是完全按照实现的预期行为.我们跟踪变量的空状态.当您最初声明变量时,该状态为 NotNull,因为您使用非空值显式初始化它.但是我们不跟踪 NotNull 的来源.例如,这实际上是等效的代码:

#nullable enable课程计划{静态无效主(){M("");}静态无效 M(字符串 s){var b = s == null;var i = s.Length;//警告 CS8602:取消引用可能为空的引用}}

在这两种情况下,您都明确测试 s 是否为 null.我们将其作为流量分析的输入,就像 Mads 在这个问题中回答的那样:https://stackoverflow.com/a/59328672/2672518.在那个答案中,结果是您在返回时收到警告.在这种情况下,答案是您会收到一条警告,提示您取消引用了可能为空的引用.

<块引用>

它不会变为可空,仅仅是因为我们愚蠢地将它与 null 进行比较.

是的,确实如此.对于编译器. 作为人类,我们可以看到这段代码,很明显地明白它不能抛出空引用异常.但是在编译器中实现可空流分析的方式,它不能.我们确实讨论了对该分析的一些改进,其中我们根据值的来源添加了额外的状态,但我们认为这为实现增加了大量的复杂性,而不会带来很大的收益,因为唯一的地方这对于这样的情况很有用,在这种情况下,用户使用 new 或常量值初始化一个变量,然后无论如何检查它的 null.

Having read this question on HNQ, I went on to read about Nullable Reference Types in C# 8, and made some experiments.

I'm very aware that 9 times out of 10, or even more often, when someone says "I found a compiler bug!" this is actually by design, and their own misunderstanding. And since I started to look into this feature only today, clearly I do not have very good understanding of it. With this out of the way, lets look at this code:

#nullable enable
class Program
{
    static void Main()
    {
        var s = "";
        var b = s == null; // If you comment this line out, the warning on the line below disappears
        var i = s.Length; // warning CS8602: Dereference of a possibly null reference
    }
}

After reading the documentation I linked to above, I would expect the s == null line to give me a warning—after all s is clearly non-nullable, so comparing it to null does not make sense.

Instead, I'm getting a warning on the next line, and the warning says that s is possible a null reference, even though, for a human, it's obvious it is not.

More over, the warning is not displayed if we do not compare s to null.

I did some Googling and I hit a GitHub issue, which turned out to be about something else entirely, but in the process I had a conversation with a contributor that gave some more insight in this behaviour (e.g. "Null checks are often a useful way of telling the compiler to reset its prior inference about a variable's nullability."). This still left me with the main question unanswered, however.

Rather than creating a new GitHub issue, and potentially taking up the time of the incredibly busy project contributors, I'm putting this out to the community.

Could you please explain me what's going on and why? In particular, why no warnings are generated on the s == null line, and why do we have CS8602 when it does not seem like a null reference is possible here? If nullability inference is not bullet-proof, as the linked GitHub thread suggests, how can it go wrong? What would be some examples of that?

解决方案

This is effectively a duplicate of the answer that @stuartd linked, so I'm not going to go into super deep details here. But the root of the matter is that this is neither a language bug nor a compiler bug, but it's intended behavior exactly as implemented. We track the null state of a variable. When you initially declare the variable, that state is NotNull because you explicitly initialize it with a value that is not null. But we don't track where that NotNull came from. This, for example, is effectively equivalent code:

#nullable enable
class Program
{
    static void Main()
    {
        M("");
    }
    static void M(string s)
    {
        var b = s == null;
        var i = s.Length; // warning CS8602: Dereference of a possibly null reference
    }
}

In both cases, you explicitly test s for null. We take this as input to the flow analysis, just as Mads answered in this question: https://stackoverflow.com/a/59328672/2672518. In that answer, the result is that you get a warning on the return. In this case, the answer is that you get a warning that you dereferenced a possibly null reference.

It does not become nullable, simply because we were silly enough to compare it with null.

Yep, it actually does. To the compiler. As humans, we can look at this code and obviously understand that it cannot throw a null reference exception. But the way the nullable flow analysis is implemented in the compiler, it cannot. We did discuss some amount of improvements to this analysis where we add additional states based on where the value came from, but we decided that this added a great deal of complexity to the implementation for not a great deal of gain, because the only places where this would be useful is for cases like this, where the user initializes a variable with a new or a constant value and then checks it for null anyway.

这篇关于当空引用似乎不可能时,为什么我们会收到可能取消引用空引用警告?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆