8

我已经习惯于检查方法的参数是否为空(然后继续抛出异常),以至于我几乎不再考虑它了。如果参数是引用类型,它就在那里:

if(arg == null)
    throw new ArgumentNullException(nameof(arg));

但是如果我要立即使用 arg 怎么办?我应该检查吗?我的意思是,如果我不这样做,无论如何,环境都会为我抛出(NullReferenceException)。

例如:

public int DoStuff(object o)
{
    return o.GetHashCode();
}

我可以轻松编写添加空检查:

public int DoStuff(object o)
{
    if(o == null)
        throw new ArgumentNullException(nameof(o));
    return o.GetHashCode();
}

但是在这两种情况下都会抛出异常(在几乎完全相同的行中,用于调试目的)。唯一的区别是类型。

问题:在具有单个引用类型参数的公共方法上,如果我要立即使用该参数,是否还应该检查它?null

4

7 回答 7

10

我建议检查,因为您有两种异常类型:

  1. 意外NullReferenceException- 出了点问题,您必须调试自己的例程
  2. ArgumentNullException-参数为空,它是错误的调用者(而不是你的代码反应正确

投掷ArgumentNullException是一种合同如果参数正确,我会做的事情

  // An exaggerated example 
  public int DoStuff(SomeObject o) {
    if (o == null)
      throw new ArgumentNullException(nameof(o));
    else if (o.Disposed)
      throw new ArgumentException(nameof(o), "o must not be disposed")  
    else if (o.Id > 100)
      throw new ArgumentOutOfRangeException("Id ...", nameof(o));  

    // o meets the contract, we accept it and thus we guarantee the output

    return o.SomeMethod();
  }

这种验证对于公共受保护)方法是典型的,因为它们暴露于外部世界并且可能面临任何争论;但是,在私有方法的情况下,您可以省略契约:任何调用者都在实现该方法的类中。

于 2016-03-09T13:31:19.600 回答
0

我认为您应该考虑几种不同的情况:

  1. 该方法是从您自己的程序中调用的。您不期望空值。您在某处使用该对象,如果它为空,它仍会更改状态。
  2. 该方法是从您自己的程序中调用的。您不期望空值。如果它是一个空值,它不会改变状态并且一切都将继续保持一致。
  3. 该方法通过库或 API 公开。一个不知名的开发者 X 用你想要的任何方式调用该方法。
  4. 您只想知道在实现内容时参数不为空。为了保护你自己和你的团队成员。

第 (1) 和 (2) 点是关于异常安全的。基本上,这表明无论您的输入如何,您的数据结构的状态都应该是一致的。在大多数情况下,这可以通过简单的检查来解决;在更复杂的情况下,它可能涉及回滚或更复杂的机器。所以:

  1. 如果您null向数据库添加一个值,您不希望它弄乱您的数据库。现在,您当然可以null通过简单地检查您的假设来解决值。如果参数是null,则抛出一个,ArgumentNullException因为这最能描述场景。
  2. 如果您调用的方法不null期望值并且它不会影响系统的其余部分,我通常会在某处发表评论并让自然运行。ANullReferenceException告诉我足够多的关于 bug 的性质。
  3. 如果它是一个 API,人们期望某些行为(例如 a ArgumentNullException)。此外,作为开发人员,您不应该相信外面的大坏世界,所以无论如何您都应该检查一下。
  4. 如果您只是想保护自己和您的团队成员,请使用Assertion. 断言通常以这样的方式实现,它们不会在发布代码中结束(或者至少对您的速度没有影响),会自动触发断点并且会使您的单元测试失败。它们也很容易识别,因此不会给您的代码增加太多臃肿。

总而言之,我不会到处放支票;它们往往会使您的代码不可读和臃肿。也就是说,如果我要检查以确保异常安全,最好使用最能描述错误的异常。

于 2016-03-09T13:53:22.370 回答
0

我想说这更多地取决于您如何处理错误,而不是如何抛出错误。一个例子会有所帮助。想象一下,这是方法而不是您所拥有的:

public static int DoStuff(string o) {
    return Int.Parse(o);
}

如果你有这个,那真的没关系。

public static void main(string[] args)
{
    try {
        Console.Write("Enter a number: ");
        value = DoStuff(Console.ReadLine());
    }
    catch(Exception ex) {
        Console.WriteLine("An exception was thrown.  Contents: " + ex.ToString());
    }
}

无论哪种方式,您的程序流程都是相同的。但是,如果您有以下情况:

public static void main(string[] args)
{
    try {
        int value = 0;
        do {
            try {
                Console.Write("Enter a non-zero number to stop: ");
                value = DoStuff(Console.ReadLine());
            }
            catch(ArgumentException ex) {
                Console.WriteLine("Problem with input, try again.  Exception message was: " + ex.Message);
                continue; // Or just nothing, will still loop
            }
        } while(value == 0);
    }
    catch(Exception ex) {
        Console.WriteLine("An exception was thrown.  Contents: " + ex.ToString());
    }
}

基本上,如果您的错误处理不关心它是哪种异常类型,那么它只是额外的代码,可能对您没有帮助。但是如果你以不同的方式处理特定的事情,那么测试并抛出更具体的异常类型可能是值得的。

但我确实同意 Dmitry 在上面的帖子,即公共方法比私有方法重要得多。您对该方法的签名有点像合同。最好强制执行。

于 2016-03-09T13:46:51.350 回答
0

好吧,这取决于;)这是我的看法,问题是(有点)主观的。

IMO,如果代码抛出 NullReferenceException,则意味着代码本身没有内部一致性。这是懒惰的表现,应该不惜一切代价避免。如果一个方法抛出 NullReferenceException,那是方法的错。

另一方面,ArgumentNullException 意味着如果参数为 null,则该方法无法“完成任务”。调用者有责任保证参数不为空,或者优雅地处理异常。

底线:ArgumentNullException 是合同,NullReferenceException 是错误。

于 2016-03-09T13:33:13.653 回答
0

我想如果你立即取消引用变量,你可以用任何一种方式进行辩论,但我仍然更喜欢 ArgumentNullException。它对正在发生的事情更加明确。异常包含为空的变量的名称,而 NullReferenceException 不包含。

于 2016-03-09T13:34:00.703 回答
0

我强烈建议您null在方法的顶部进行检查。

将其视为指定以下内容的“合同”:

为了执行此方法,此参数必须为非空。

做空检查是很好的文档。您可以更进一步使用 XML 注释,例如

/// <summary>
/// 
/// </summary>
/// <param name="arg1"></param>
/// <exception cref="ArgumentNullException">If <paramref name="arg1"/> is NULL.</exception>
private static void Foo(object arg1)
{
}
于 2016-03-09T13:37:23.560 回答
-1

跟随爱因斯坦——“让事情尽可能简单,但不要更简单”。该代码是否在没有一行代码的情况下做同样的事情?如果是这样,最好将其删除。

于 2016-03-09T13:26:26.167 回答