提问人:Razor23 Donetsk 提问时间:2/23/2023 最后编辑:Razor23 Donetsk 更新时间:3/7/2023 访问量:173
为什么明显的 NullReferenceException 不是编译时错误?[关闭]
Why isn't obvious NullReferenceException a compile-time error? [closed]
问:
令我感到惊讶的是,以前没有人问过这个问题,但为什么编译器在如此明目张胆的情况下将自己限制在警告上?
object obj = null;
string str = obj.ToString(); //must be a compile-time error?
在反编译代码并看到以下内容后,我尤其无法理解它:
((object) null).ToString();
我检查了相似(但不同)的 SO 问题(1 和 2),答案归结为“无法在编译时检查变量的值”。
但为什么这是不可能的呢?我在这里错过了什么?编译器能看到它产生的结果吗?
答:
为什么它是警告而不是错误,简短的回答是,空引用分析不是万无一失的,不能轻易考虑并行或间接引用。代码本身满足编译和执行的所有编译器规则。在运行时执行失败的事实不一定是错误,它只是应用程序可能处理也可能不处理的异常。
- 开发人员可能特别希望诱发运行时错误。
编译器不能假定开发人员不知道这将导致运行时错误。一旦我们开始这样做,我们还可能犯错哪些其他假设?
错误是针对真正的代码冲突。变量的值 这一事实需要对该变量在运行时的预期有特定的了解。只有在执行时,我们才能确定 的值为 null,到目前为止的所有其他内容充其量只是一个假设。编译器可能有意见的所有其他内容(如约定和代码样式)都将成为警告或注释。由每个开发人员或团队决定是否要将特定警告提升为错误状态,或者是否将所有警告视为错误。null
state
obj.ToString()
obj
从另一个角度思考这个问题可能会有所帮助。您的示例表示 2 行代码,它们恰好在今天的脚本中彼此相邻存在:
object obj = null;
string str = obj.ToString();
在设计时,可以想象这两个语句之间可能会存在更多的代码,这样做的最终效果是,额外的逻辑可能会将非 null 值设置为:obj
object obj = null;
...
obj = "hello world!";
...
string str = obj.ToString();
但是并行处理呢,如果从并行运行的不同逻辑块设置,这样在实例化和对值的调用之间设置在我们在此处看到的代码之外呢?obj
obj
obj.ToString()
...如果调用的方法是扩展方法,该怎么办?毕竟,扩展方法可以从 null 引用调用。
.ToString()
很明显,它会引发运行时错误,因为它是一个成员声明的方法,并且我们可以确定该对象尚未初始化。有很多可能性需要检查,在大型代码库中,需要为每个变量和每个调用花费这种努力。这种类型的功能在 C# 编译器的原始版本中没有。
20 年前,即使我们可以,在标准硬件上对代码进行这种级别分析也会大大降低开发人员的工作效率。良好的 SDLC 实践(例如在发布之前测试代码)无论如何都会发现这些类型的问题,从编译器那里获得这种级别的检查根本不是优先事项。
快进到今天,编译器已经发展,开发人员已经消除了所有错误,并能够专注于运行时和生产力的改进。其中一项改进是可为空的引用类型分析。这几乎涵盖了通过建立一组开发人员需要遵守的新约定来检查您的场景和非常复杂的场景。
如果您有兴趣将旧代码迁移到 NRT,请参阅使用可为 null 的引用类型更新代码库以改进 null 诊断警告,但请注意,许多现有代码库在首次启用此功能时将有数千个警告。如果你可以忽略警告,那么我不确定你为什么要打扰,对于我们其他人来说,将警告视为错误,你将有很多代码需要审查、修复或注释。
我将指出为什么我需要在 csporj 中启用 NUllable 元素以使用可为 null 的引用类型?,Jeroen Mostert 的这条评论是对为什么这不是错误的最佳总结,即使 NRT 意味着我们可以合理地假设这是一个错误:(强调是我的)
至于它没有任何区别,那是设计使然。NRT 经过专门和精心设计,对现有代码的影响最小,除非您明确选择加入,以确保该功能可以实际增加价值并逐渐与现有代码库集成,而不是采用全有或全无的方法,因为没有人愿意在以前工作良好的代码中预先修复 200 个警告。这也是为什么,例如,写字符串?未启用 NRT 的地方并不完全被视为错误。
如果您在 2019 年错过了它,您应该阅读拥抱可为 null 的引用类型。我们中的许多人还没有完全接受这一点,还有更多的遗留代码库足够稳定或具有足够强大的测试,因此重写代码库以利用 NRT 的价值较小或没有价值,事实上,这样做可能会对产品造成重大风险,因为我们应该在发布更新的代码之前重新测试所有已更改的内容。
评论
int i = 10 / 0
is different, because literals are used the expression is immutable and can be evaluated at compile time. NRTs are interpreted and often open to interpretation, as such there is a degree of uncertainty, this I tried to explain in my post is the most important reason that they cannot be errors by default. If I had to choose your feature over all the others I want to see implemented in C#, identifying this one as an error goes to the bottom of the list for me, it is already a warning, so the compiler is already doing this check for you, can't ask for much else.
[System.Runtime]System.Object::ToString()
评论