在 C# 8 中,如何检测不可能的空值检查?

In C# 8, how do I detect impossible null checks?

我已经开始在 C# 8 中使用可为 null 的引用类型。到目前为止,除了一件小事外,我很喜欢这些改进。

我正在迁移一个旧代码库,其中充满了很多冗余或无法访问的代码,例如:

void Blah(SomeClass a) {
  if (a == null) {
    // this should be unreachable, since a is not nullable
  }
}

遗憾的是,我没有看到任何可以为我标记此代码的警告设置!这是 Microsoft 的疏忽,还是我遗漏了什么?

我也使用 ReSharper,但它的 none 警告设置似乎也捕捉到了这一点。还有其他人找到解决方案吗?

编辑:我知道从技术上讲这仍然可以实现,因为可空性检查不是万无一失的。这不是重点。在这种情况下,我将参数声明为 NOT nullable,检查它是否为空通常是错误的。在极少数情况下,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.

鉴于此,通常认为最佳做法是继续在任何 publicprotected 成员中提供保护子句和其他可空性检查。

其实如果你因为以上顾虑使用微软的Code Analysis package—which I’d recommend—it will warn you to use a guard clause in this exact situation. They considered removing this for code in C# 8’s nullability context, but 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# 的不可空引用类型不必要的空检查。

简短的回答是你不能;在这一点上,Roselyn 的静态流分析专注于识别取消引用空对象的可能性,而不是检测潜在的无关检查。

不过,如上所述,长答案是你 不应该;在 Microsoft adds runtime validation 或要求可空性上下文之前,这些空检查会继续提供价值。