简体   繁体   English

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

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

I've started using nullable reference types in C# 8. So far, I'm loving the improvement except for one small thing.我已经开始在 C# 8 中使用可为空的引用类型。到目前为止,除了一件小事之外,我很喜欢这种改进。

I'm migrating an old code base, and it's filled with a lot of redundant or unreachable code, something like:我正在迁移一个旧代码库,其中充满了许多冗余或无法访问的代码,例如:

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

Unfortunately, I don't see any warning settings that can flag this code for me, Was this an oversight by Microsoft?不幸的是,我没有看到任何可以为我标记此代码的警告设置,这是 Microsoft 的疏忽吗? or am I missing something?还是我错过了什么?

I also use ReSharper, but none of its warning settings appear to capture this either.我也使用 ReSharper,但它的警告设置似乎也没有捕捉到这一点。 Has anybody else found a solution to this?有没有其他人找到解决这个问题的方法?

Edit: I'm aware that technically this is still reachable because the nullability checks aren't bulletproof.编辑:我知道从技术上讲这仍然是可以实现的,因为可空性检查不是防弹的。 That's not really the point.这不是重点。 In a situation like this, where I declare a paramater as NOT nullable , it is a usually a mistake to check if it's null.在这种情况下,我将参数声明为NOT nullable ,检查它是否为 null 通常是一个错误。 In the rare event that null gets passed in as a non-nullable type, I'd prefer to see the NullReferenceException and track down the offending code that passed in null by mistake.在极少数情况下 null 作为不可为空的类型传入,我希望看到NullReferenceException并追踪错误地传入 null 的违规代码。

It's really important to note that not only are the nullability checks not bullet proof, but while they're designed to discourage callers from sending null references, they do nothing to prevent it.值得注意的是,可空性检查不仅不是防弹的,而且虽然它们旨在阻止调用者发送null引用,但它们并没有采取任何措施来阻止它。 Code can still compile that sends a null to this method, and there isn't any runtime validation of the parameter values themselves.向此方法发送null的代码仍然可以编译,并且参数值本身没有任何运行时验证。

If you're certain that all callers will be using C# 8's nullability context—eg, this is an internal method— and you're really diligent about resolving all warnings from Roslyn's static flow analysis (eg, you've configured your build server to treat them as errors) then you're correct that these null checks are redundant.如果您确定所有调用者都将使用 C# 8 的可空性上下文(例如,这是一种internal方法)并且非常努力地解决来自 Roslyn 的 static 流分析的所有警告(例如,您已将构建服务器配置为将它们视为错误)那么您是正确的,这些 null 检查是多余的。

As noted in the migration guide , however, any external code that isn't using C# nullability context will be completely oblivious to this:然而,如迁移指南中所述,任何不使用 C# 可空性上下文的外部代码都将完全忽略这一点:

The new syntax doesn't provide runtime checking.新语法不提供运行时检查。 External code might circumvent the compiler's flow analysis.外部代码可能会绕过编译器的流分析。

Given that, it's generally considered a best practice to continue to provide guard clauses and other nullability checks in any public or protected members.鉴于此,在任何publicprotected成员中继续提供保护条款和其他可空性检查通常被认为是最佳实践。

In fact, if you use Microsoft's 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 due to the above concerns.他们考虑在 C# 8 的可空性上下文中删除此代码,但由于上述问题决定保留它

When you get these warnings from Code Analysis, you can wrap your code in a null check, as you've done here.当您从代码分析中收到这些警告时,您可以将代码包装在 null 检查中,就像您在此处所做的那样。 But you can also throw an exception.但是你也可以抛出异常。 In fact, you could throw another NullReferenceException —though that's definitely not recommended.事实上,您可以抛出另一个NullReferenceException ——尽管这绝对不推荐。 In a case like this, you should instead throw an ArgumentNullException , and pass the name of the parameter to the constructor:在这种情况下,您应该改为抛出ArgumentNullException ,并将参数名称传递给构造函数:

void Blah(SomeClass a) {
  if (a == null) {
    throw new ArgumentNullException(nameof(a));
  }
  …
}

This is much preferred over throwing a NullReferenceException at the source because it communicates to callers what they can do to avoid this scenario by explicitly naming the exact parameter (in this case) that was passed as a null .这比在源头抛出NullReferenceException更可取,因为它通过显式命名作为null传递的确切参数(在这种情况下)与调用者沟通他们可以做些什么来避免这种情况。 That's more useful than just getting a NullReferenceException —and, possibly a reference to your internal code—where the exception occurred.这比仅仅获得NullReferenceException更有用——并且可能是对您的内部代码的引用——发生异常的地方。

Critically, this exception isn't meant to help you debug your code— that's what Code Analysis is doing for you .至关重要的是,此异常并不是为了帮助调试代码——这就是代码分析正在为您做的事情 Instead, it's demonstrating that you've already identified the potential dereference of a null value, and you've accounted for it at the source .相反,它表明您已经确定了 null 值的潜在取消引用,并且您已经在源头考虑了它。

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)); }

This is a really roundabout way of answering your original question, which is how to detect the presence of null checks made unnecessary by C#'s non-nullable reference types.这是回答您原始问题的一种非常迂回的方式,即如何检测 C# 的不可为空引用类型不必要的 null 检查的存在。

The short answer is that you can't;简短的回答是你不能; at this point, Roselyn's static flow analysis is focused on identifying the possibility of dereferencing null objects, not detecting potentially extraneous checks.此时,Roselyn 的 static 流分析的重点是识别取消引用 null 对象的可能性,而不是检测潜在的无关检查。

The long answer, though, as outlined above, is that you shouldn't ;但是,如上所述,长答案是您不应该 until Microsoft adds runtime validation , or mandates the nullability context, those null checks continue to provide value.在 Microsoft添加运行时验证或强制可空性上下文之前,这些 null 检查将继续提供价值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM