我已经开始在 C# 8 中使用可为 null 的引用类型。到目前为止,除了一件小事之外,我很喜欢这种改进。
我正在迁移一个旧的代码库,它充满了很多冗余或无法访问的代码,例如:
void Blah(SomeClass a) {
if (a == null) {
// this should be unreachable, since a is not nullable
}
}
不幸的是,我没有看到任何可以为我标记此代码的警告设置!这是微软的疏忽,还是我遗漏了什么?
我也使用 ReSharper,但它的警告设置似乎也没有捕捉到这一点。有没有其他人找到解决这个问题的方法?
编辑:我知道从技术上讲这仍然可以访问,因为可空性检查不是防弹的。这不是重点。在这种情况下,我将参数声明为 不可为空 ,检查它是否为空通常是一个错误。在 null 作为不可空类型传入的罕见事件中,我更愿意看到
NullReferenceException
并追踪错误传入 null 的违规代码。
最佳答案
值得注意的是,可空性检查不仅不是防弹的,而且它们旨在阻止调用者发送 null
引用,他们没有做任何事情来阻止它。发送 null
的代码仍然可以编译到此方法,并且没有对参数值本身进行任何运行时验证。
如果您确定所有调用者都将使用 C# 8 的可空性上下文——例如,这是一个 internal
方法— 和 您真的很努力地解决了 Roslyn 静态流分析中的所有警告(例如,您已将构建服务器配置为将它们视为错误),那么您正确地认为这些空检查是多余的。
如 the migration guide 中所述但是,任何不使用 C# 可空性上下文的外部代码都将完全忽略这一点:
The new syntax doesn't provide runtime checking. External code might circumvent the compiler's flow analysis.
鉴于此,在任何
public
中继续提供保护条款和其他可空性检查通常被认为是最佳实践。或 protected
成员。事实上,如果你使用微软的 Code Analysis package——我推荐它——它会警告你在这种情况下使用保护子句。他们考虑为 C# 8 的可空性上下文中的代码删除它,但是 decided to maintain it由于上述顾虑。
当您从代码分析中收到这些警告时,您可以将您的代码包装在空检查中,就像您在此处所做的那样。但是你也可以抛出异常。事实上,你可以再扔一个
NullReferenceException
——虽然绝对不推荐。在这种情况下,您应该抛出 ArgumentNullException
,并将参数的名称传递给构造函数:void Blah(SomeClass a) {
if (a == null) {
throw new ArgumentNullException(nameof(a));
}
…
}
这比抛出
NullReferenceException
更受欢迎在源代码中,因为它通过显式命名作为 null
传递的确切参数(在本例中)来向调用者传达他们可以做些什么来避免这种情况。 .这比仅仅获得 NullReferenceException
更有用- 并且,可能是对您的内部代码的引用 - 发生异常的地方。至关重要的是,此异常并不是要帮助您调试代码——这正是代码分析为您所做的。相反,它表明您已经确定了对空值的潜在取消引用,并且您已经在源头考虑了它。
Note: These guard clauses can add a lot of clutter to your code. My preference is to create a reusable internal utility that handles this via a single line. Alternatively, a single-line shorthand for the above code is:
void Blah(SomeClass a) { _ = a?? throw new ArgumentNullException(nameof(a)); }
这是回答您的原始问题的一种非常迂回的方式,即如何检测 C# 的不可空引用类型所不需要的空检查的存在。
简短的回答是你不能;在这一点上,Roslyn 的静态流分析侧重于识别取消引用空对象的可能性,而不是检测潜在的无关检查。
但是,如上所述,答案很长,您不应该这样做;直到微软 adds runtime validation ,或要求可空性上下文,这些空检查继续提供值(value)。
关于c# - 在 C# 8 中,如何检测不可能的空检查?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61000518/