C# 何时为空检查启用了可空引用类型的参数

C# 何时为空检查启用了可空引用类型的参数,c#,c#-8.0,nullable-reference-types,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)); ... } 这些代码都不是公共API的一部分,因此我

给定一个使用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));
    ...
}
这些代码都不是公共API的一部分,因此我怀疑这些检查可能是多余的。这两个参数没有标记为null,因此如果有调用代码传入null,编译器应该发出警告

给定一个使用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));
    ...
}
这取决于您对通过API的所有路径的确定程度。考虑这个代码:

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

private void FooImpl(string x)
{
    ...
}
此处
FooImpl
不是公共API的一部分,但如果
Foo
未验证其参数,则仍可接收空引用。(实际上,它可能依赖
Foo
来执行参数验证。)

签入
FooImpl
当然不是多余的,因为它在执行时执行编译器在编译时无法绝对确定的检查。可为空的引用类型提高了一般安全性,更重要的是提高了代码的表达能力,但它们与CLR提供的类型安全性不同(例如,为了阻止您将
字符串
引用视为
类型
引用)。编译器在执行时对某个特定表达式是否为空的看法可能有多种“错误”的方式,并且可以使用
覆盖编译器无论如何

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

因此,如果您的公共API正在执行所有适当的参数检查(
Foo
,在上面的示例中),那么代码中的检查已经是多余的。你对此有多大信心?如果你绝对自信,而且犯错的影响很小,那么一定要摆脱验证。C#8特性可能会帮助您在这方面获得信心,但您仍然需要小心,不要过于自信——毕竟,上面的代码不会给出任何警告


就我个人而言,在为C#8更新Noda Time时,我不会删除任何参数验证。

如果您可以控制将调用此方法的整个代码库,并且所有代码都是使用C#8编译的,并且启用了此功能,并且您非常努力地修复编译器生成的所有警告/错误,那么您可以删除这些If语句。如果您对这些要求中的任何一个不确定,那么您应该保留If-statements。通过现在删除的答案中的注释,我很清楚,您不想了解编译器检查,但您在询问其他人的观点。因此,我投票结束这一问题,因为这主要是基于意见。正如答案和评论中所述,编译器检查并不能替代if语句或契约系统,而且很容易绕过。这个风险对你来说是否足够完全取决于你。然而,您似乎已经知道新的编译器检查的作用。我已经使用JetBrains注释和ReSharper/Rider有相当长的一段时间了,它们可以帮助您确信您的代码正在做正确的事情,但它们不能替代实际的检查,所以我两者都有。在出现可为null的引用类型之后,我将继续使用这两种类型,因为我编写的代码可能会被未经这些检查编译的代码使用,或者该代码的编写者甚至可能直接对编译器撒谎。(大家都知道,当我觉得有必要的时候,我会在这里或那里使用这种奇怪的技巧)。编译器不能对任何调用代码发出警告——有转义图案填充和重写(如空原谅运算符)会阻碍它的分析。是否添加这些检查应该在很大程度上独立于是否使用可为空的引用——如果您已经确信没有这些检查,您可以继续不使用它们;如果你没有,那么你应该把它们留在家里。新的检查减少了引入错误的可能性。他们是否让你不再需要他们变得不太可能,这将取决于你。@LasseVågsætherKarlsen这是一种明确鼓励的基于观点的问题。这就是
许多好的问题根据专家经验产生一定程度的意见
。此时,许多人知道该特性的描述,但很少有人知道编译器的实际限制。更少的人有迁移大型项目以使用可空引用的经验。我能说出3个,其中一个已经回答了。另外两个在SOW工作。如果(arg为null)抛出不可为null的引用类型方法参数的IL指令,那么不让C#8.0编译器生成
if(arg为null)的理由是什么?如果编译器在默认情况下生成该代码不是很好吗?@Steven:这会改变代码的行为,使启用该功能的风险更大。有一条坚定的路线是“打开特性不会改变生成的IL”,这使得推理变得非常容易,这意味着你不会仅仅因为你的期望“可能是无害的”而破坏你的代码。我希望有语法可以让你表达“这个参数是非空的,请帮我检查一下”,但我支持默认情况下不这么做的决定。谢谢Jon,这很有意义。不过,我不确定您所说的其他语法。我更愿意看到编译器开关/项目设置启用此功能(对于新创建的C#8项目,默认情况下最好启用)。据我所知,C#8引用类型不可为空。我仍然不明白,如果变量不能为空,为什么我需要检查null?@You