何时检查空值

时间:2009-09-28 19:04:18

标签: c# exception-handling

这有点是一个开放式的问题,但我正试图通过异常处理方面的优秀实践来提高我的技能,特别是在检查空值时。

我知道什么时候检查空值但是我不得不说有一半时间我没有,这对我很困扰。我知道除非你使用可以为空的int,否则不能将int设置为null。我知道字符串可以设置为空或空,因此您可以检查IsNullOrEmpty。

显然,在构造函数中,您还希望在那里添加显式检查。这是给定的。而且我认为你应该在将数组,通用对象或其他对象传入一个基本上可以设置为null的方法时检查null吗?

但是这里有更多的异常处理。例如,我不知道何时总是检查并在代码中明确地抛出空引用异常。如果我有传入的参数,通常它很直接,但总会有我问自己的情况,是否需要明确的空投?

我真的没有具体的例子,但想知道是否有一个很好的参考,真正讨论异常处理参考何时抛出它们(在方法,类中,你命名它)。

6 个答案:

答案 0 :(得分:15)

你不应该抛出NullReferenceException。如果它是一个null throw ArgumentNullException的参数。

我更喜欢为public / protected方法的所有引用类型参数检查null。对于私有方法,如果您确定所有呼叫始终具有有效数据,则可以省略检查。

答案 1 :(得分:6)

除非您使用代码约定,否则我认为检查任何公共/受保护成员的参数是一种好习惯,并且明确记录它们是否可以为空。对于私有/内部方法,它变成了验证您自己的代码而不是其他人的问题......这是一个判断调用。

我有时会使用辅助扩展方法,所以我可以写:

public void Foo(string x, string y, string z)
{
    x.ThrowIfNull("x");
    y.ThrowIfNull("y");
    // Don't check z - it's allowed to be null
    // Method body here
}

ThrowIfNull将使用适当的名称抛出ArgumentNullException

通过在之前显式检查进行任何其他调用,您知道如果抛出异常,则不会发生任何其他情况,因此您不会有损坏的状态。

我承认我会省略这些支票,我知道第一次通话会做同样的检查 - 例如如果它是一个超载捎带到另一个上面。

使用代码合同,我写道:

public void Foo(string x, string y, string z)
{
    Contract.Requires(x != null);
    Contract.Requires(y != null);

    // Rest of method here
}

这将抛出ContractException而不是ArgumentNullException,但所有信息仍然存在且无论如何都不应该明确地捕捉ArgumentNullException(除了可能应对第三方之外)不良)。

当然,您是否允许空值的决定是完全不同的事情。这完全取决于具体情况。防止空值进入你的世界确实会让事情变得更容易,但同时空值本身也是有用的。

答案 2 :(得分:6)

我在何时检查空值的经验法则是:

  • 始终检查传递给任何类的公共/受保护方法的参数。
  • 始终检查构造函数的参数并初始化方法。
  • 始终检查索引器或属性设置器中的参数。
  • 始终检查作为接口实现的方法的参数。
  • 如果有多个重载,请尝试将所有参数前提条件放在其他重载委托给它的单个方法中。

空参数的早期通知(更接近错误点)比远离错误点的随机NullReferenceExceptions要好得多。

您可以使用实用程序方法清理典型的if( arg == null ) throw new ArgumentNullException(...);构造。您可能还希望在C#4.0中查看code contracts作为改进代码的方法。代码契约执行静态和运行时检查,这有助于即使在编译时也能识别问题。

通常,为方法编写前提条件是一项耗时(因此经常省略)的实践。但是,这种类型的防御性编码的好处是可能节省时间调试red herrings,因为您没有验证输入。

作为旁注,ReSharper是一个很好的工具,用于识别对在运行时可能为null的参数或本地成员的潜在访问。

答案 3 :(得分:1)

越早越好。你越早捕获(无效)null,你就越不可能在某个关键过程中抛出。另一件需要考虑的事情是使用您的属性(setter)来集中验证。我经常在构造函数中引用我的属性,因此我可以很好地重用该验证。

class A
{
  private string _Name
  public string Name
  {
    get { return _Name; }
    set 
    {
      if (value == null)
         throw new ArgumentNullException("Name");
      _Name = value;
    }
  }

  public A(string name)
  {
     //Note the use of property with built in validation
     Name = name;
  }
}

答案 4 :(得分:0)

取决于您的方法类型/ api / library / framework。我认为私有方法没问题,他们不检查空值,除非它们是一种断言方法。

如果您使用“非null”测试防御并乱丢您的代码 - 测试或“if” - 陈述您的代码将无法读取。

通过提交错误/空/空参数来定义客户端可能弄乱代码的区域。如果你知道在哪里,你可以画出在哪里进行空检查。在你的api入口点就像现实生活中的安全工作一样。

想象一下,每一分钟都会为每部电影都会在电影院里出示你的票而烦恼。

答案 5 :(得分:0)

我更喜欢使用静态模板方法来检查输入约束。这种情况的一般格式如下:

static class Check {
    public static T NotNull(T arg) {
        if( arg == null ) throw new ArgumentNullException();
    }
}

使用它的好处是你的方法或构造函数现在可以用Check.NotNull()作为示例包装第一次使用参数:

this.instanceMember = Check.NotNull(myArgument);

当我到达.Net 4时,我可能会转换为代码合同,但在此之前这是有效的。顺便说一下,您可以在以下网址找到我更完整的Check类定义:

http://csharptest.net/browse/src/Shared/Check.cs