C# C语言中的空参数检查#

C# C语言中的空参数检查#,c#,function,null,C#,Function,Null,在C#中,在null不是有效值的每个函数中添加参数null检查有什么好的理由(除了更好的错误消息)吗?显然,使用s的代码无论如何都会抛出异常。这样的检查使得代码更慢,更难维护 void f(SomeType s) { if (s == null) { throw new ArgumentNullException("s cannot be null."); } // Use s } 主要的好处是,您从一开始就明确了方法的需求。这使其他开发人员可以清楚地看到,调用程序向

在C#中,在null不是有效值的每个函数中添加参数null检查有什么好的理由(除了更好的错误消息)吗?显然,使用s的代码无论如何都会抛出异常。这样的检查使得代码更慢,更难维护

void f(SomeType s)
{
  if (s == null)
  {
    throw new ArgumentNullException("s cannot be null.");
  }

  // Use s
}

主要的好处是,您从一开始就明确了方法的需求。这使其他开发人员可以清楚地看到,调用程序向方法发送空值确实是一个错误


该检查还将在执行任何其他代码之前停止该方法的执行。这意味着您不必担心该方法所做的修改未完成。

是的,有很好的理由:

  • 它准确地标识空值,这可能在
    NullReferenceException
  • 它使代码在无效输入时失败,即使某些其他条件意味着该值未被取消引用
  • 它使异常在方法可能产生任何其他副作用之前发生,您可能会在第一次取消引用之前达到这些副作用
  • 这意味着您可以确信,如果您将参数传递到其他内容中,您不会违反他们的约定
  • 它记录了您的方法的需求(当然,使用它更好)
至于你的反对意见:

  • 速度较慢:您是否发现这实际上是代码中的瓶颈,或者您是在猜测?空值检查非常快速,在绝大多数情况下,它们不会成为瓶颈
  • 这使得代码更难维护:我认为恰恰相反。我认为使用代码更容易,因为代码清楚地表明了参数是否可以为null,并且您确信该条件是强制执行的
对于你的断言:

显然,使用s的代码无论如何都会抛出异常

真的吗?考虑:

void f(SomeType s)
{
  // Use s
  Console.WriteLine("I've got a message of {0}", s);
}
它使用
s
,但不会引发异常。如果
s
为null是无效的,并且表示出现了问题,那么异常是这里最合适的行为

现在,把这些参数验证检查放在哪里是另一回事了。您可能决定信任自己类中的所有代码,因此不必担心私有方法。您可能决定信任程序集的其余部分,因此不必担心内部方法。几乎可以肯定,您应该验证公共方法的参数

旁注:
ArgumentNullException
的单参数构造函数重载应该只是参数名,因此您的测试应该是:

if (s == null)
{
  throw new ArgumentNullException("s");
}
或者,您可以创建一个扩展方法,允许使用更简洁的:

s.ThrowIfNull("s");
在我的(泛型)扩展方法版本中,我让它在非null时返回原始值,允许您编写如下内容:

this.name = name.ThrowIfNull("name");

如果你不太在意的话,你也可以有一个不带参数名的重载。

如果没有一个明确的
if
检查,如果你不拥有代码,就很难找出什么是
null

如果您在没有源代码的库中得到
NullReferenceException
,那么您很可能很难弄清楚自己做错了什么

这些
if
检查不会使代码明显变慢


请注意,的参数是参数名,而不是消息。
你的代码应该是

如果(s==null)抛出新的ArgumentNullException(“s”);
我编写了一个代码片段以简化此过程:


检查空参数
tna
抛出新ArgumentNullException的代码段
渣
膨胀
周边地区
参数
检查空值的参数
价值



当您遇到异常时,它会保存一些调试

ArgumentNullException明确声明为空的是“s”


如果您没有进行该检查并让代码崩溃,那么您将从该方法中的某个未标识行获得一个NullReferenceException。在发布版本中,你不会得到行号

我同意乔恩的观点,但我想补充一点

我对何时添加显式空检查的态度基于以下前提:

  • 单元测试应该有一种方法来练习程序中的每一条语句
  • throw
    语句是语句
  • if
    的结果是一条语句
  • 因此,如果(x==null)抛出任何东西,应该有一种方法来练习
    中的
    抛出
如果无法执行该语句,则无法对其进行测试,应替换为
Debug.Assert(x!=null)

如果有可能执行该语句,则编写该语句,然后编写执行该语句的单元测试

尤其重要的是,公共类型的公共方法以这种方式检查它们的参数;你不知道你的用户会做什么疯狂的事情。尽快给他们“嘿,你这个笨蛋,你做错了!”例外


相比之下,私有类型的私有方法更可能处于控制参数的情况下,并且可以有力地保证参数永远不为null;使用断言来记录该不变量。

如果需要更好的方法来确保不获取任何空对象作为参数,您可能需要看看

原始代码:

void f(SomeType s)
{
  if (s == null)
  {
    throw new ArgumentNullException("s cannot be null.");
  }

  // Use s
}
将其改写为:

void f(SomeType s)
{
  if (s == null) throw new ArgumentNullException(nameof(s));
}
使用int i = Age ?? 0;
if (age == null || age == 0)
if (age.GetValueOrDefault(0) == 0)
if ((age ?? 0) == 0)
int i = age.HasValue ? age.Value : 0;