何时对启用了可空引用类型的参数进行空检查

Cal*_*ins 10 c# c#-8.0 nullable-reference-types

鉴于使用C#8.0的可空引用类型功能的程序中的函数,我是否仍应对参数执行空检查?

void Foo(string s, object o)
{
    if (s == null) throw new ArgumentNullException(nameof(s)); // Do I need these?
    if (o == null) throw new ArgumentNullException(nameof(o));
    ...
}
Run Code Online (Sandbox Code Playgroud)

这些代码都不是公共API的一部分,所以我怀疑这些检查可能是多余的.这两个参数未标记为可为空,因此编译器应警告任何调用代码是否可以传入null.

Jon*_*eet 13

鉴于使用C#8.0的可空引用类型功能的程序中的函数,我是否仍应对参数执行空检查?

这取决于您通过API的所有路径的确定程度.考虑以下代码:

public void Foo(string x)
{
    FooImpl(x);
}

private void FooImpl(string x)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

FooImpl不是公共API的一部分,但如果Foo不验证参数,仍然可以接收空引用.(实际上,它可能依赖于Foo执行参数验证.)

检查中FooImpl肯定不是多余的,它的执行在执行时检查,编译器不能是绝对有把握在编译时.可空的引用类型提高了一般安全性,更重要的是提高了代码的表现力,但它们与CLR提供的类型安全性不同(例如,阻止您将string引用视为Type参考).编译器有多种方式可以"错误"查看特定表达式在执行时是否为空的视图,并且!无论如何都可以覆盖编译器.

更广泛地说:如果您的检查 C#8 之前没有冗余,那么它们 C#8 之后就不是冗余的,因为可空的引用类型功能不会改变为代码生成的IL而不是属性.

因此,如果您的公共API正在执行所有适当的参数检查(Foo在上面的示例中),那么代码中的检查已经是多余的.你对此有多自信?如果你绝对自信并且错误的影响很小,那么肯定 - 摆脱验证.C#8功能可以帮助你对此有所信心,但你仍需要小心你不要自信 - 毕竟 - 上面的代码不会给出任何警告.

我个人在更新Noda Time for C#8时没有删除任何参数验证.

  • @Steven:这将改变代码的行为,使打开该功能的风险更大。有一条坚定的路线“打开功能不会更改生成的IL”,这使得推理起来很容易,并且这意味着您不会因为“期望结果是错误的”而错误地破坏了代码无害”的方式。我希望将有语法允许您表达“此参数为非null,请为我检查”,但是我支持默认情况下不执行此操作的决定。 (5认同)
  • *不*让 C# 8.0 编译器为不可为空的引用类型方法参数生成 `if (arg is null) throw` IL 指令的理由是什么?如果编译器默认生成该代码不是很好吗? (2认同)
  • 谢谢乔恩,这是有道理的。不过,我不确定您正在谈论的其他语法。我宁愿看到启用此功能的编译器开关/项目设置(最好为新创建的 C# 8 项目默认启用)。 (2认同)
  • @Youssef13:运行时不会阻止引用为空。您只会收到警告。因此,变量“不会为空”或“变量不能为空”并不是这种情况。如果您的代码是唯一可以为特定路径提供值的代码,并且您绝对确定您永远不会传入 null(或者该值是派生的),那么当然,请删除检查 - 但如果该值可以来自在其他地方(例如,调用者提供参数),那么您应该检查,因为它*可能*为空。 (2认同)