何时检查空值

5

这是一个开放性问题,但我想通过良好的异常处理实践来提高我的技能,具体来说就是什么时候需要检查 null 值。

我大多数情况下都知道何时需要检查 null 值,但必须承认有一半的时间我并不知道,这让我很困扰。我知道整型不能设置为 null,除非使用可空整型。我知道字符串可以设置为 null 或空值,因此可以检查 IsNullOrEmpty。

显然,在构造函数中,您也要添加显式检查。这是必须的。我认为,每当将数组、通用对象或其他对象传递到可能设置为 null 的方法中时,您应该检查 null 值,对吗?

但是,异常处理还有更多内容。例如,我不知道什么时候总是需要在代码中显式检查和抛出 null 引用异常。如果我有传入参数,通常情况下很简单,但总会有一些情况让我自问:是否需要显式抛出 null 异常?

我没有具体的例子,但想知道是否有一个好的参考资料,真正讨论与异常处理相关的内容,例如何时抛出异常(在方法、类中等)。

6个回答

15

应该避免抛出 NullReferenceException。如果参数为 null,则应抛出 ArgumentNullException

我倾向于在公共/受保护的方法中对所有引用类型参数进行空值检查。对于私有方法,如果您确定所有调用都将始终具有有效数据,则可以省略检查。


1
同样地,我总是检查可以被我没有编写的代码访问的方法参数上的null,并抛出ArgumentNullException。(当然,如果null是有效的参数,则除外。)如果我不这样做,调用者将会得到一个NullReferenceException,这基本上意味着“你调用的代码是一个有缺陷的混乱”。对于私有代码,我宁愿使用Debug.Assert而不是抛出异常。在调试版本中,它具有相同的错误报告功能,在发布版本中,错误永远不会发生。 - Joren
1
除非我将参数存储在成员中(如果我要创建公共API,我也会这样做),否则我从不检查。说真的,当所有东西都在同一个解决方案中时,你是否得到NullReferenceException(=方法内没有其他代码)或ArgumentNullException(=方法内有其他代码)并不重要。 - erikkallen
如果您正在检查IsNullOrEmpty,那么应该抛出什么样的异常? - PositiveGuy
@coffeeaddict:对于String.IsNullOrEmpty,我会抛出同样的ArgumentNullException。在这里可以看到一个相关的问题:https://dev59.com/pnM_5IYBdhLWcg3wfTO7 - Ahmad Mageed
@Bobby,如果你确定它不会为空,为什么要增加额外开销呢?例如,一个公共方法先进行检查,然后调用私有实现。为什么还要再次检查呢?你可以推理出私有方法的使用情况。 - Brian Rasmussen
如果方法非常短且没有做太多事情,那么几乎没有什么区别。不幸的是,我必须处理 A.B.C.D.F() 这样的代码。对于这些情况,NullReferenceException 相当令人恼火。 - Brian Rasmussen

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。当然,是否允许空值完全取决于情况。防止 null 值进入您的世界确实使某些事情变得更容易,但与此同时,null 值本身也可以很有用。

值得一提的是,.NET代码合同的一个重要优点是它们能够提供编译时(静态)分析的水平,以识别可能将null传递给方法的情况。在编译时捕获问题总比在运行时更好。 - LBushkin
@LBushkin:当然,只要你有Team System(或者学术许可证)。 - Jon Skeet
没错。但现在谁还不用团队系统呢?我是说,每个席位只需要花费50万美元左右,或者类似的数字。但愿这个功能最终能够被纳入 Visual Studio 的核心中。 - LBushkin
.NET Framework 4有哪些新功能:http://msdn.microsoft.com/zh-cn/library/dd409230(VS.100).aspx 它们列出了代码合约 :) - Joren
@Joren:是的,代码合同在.NET 4.0中(有点;核心是),但静态检查器仅限于Team System用户。 - Jon Skeet

6

我检查null的经验法则如下:

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

早期通知null参数(靠近错误点)比远离错误点发生的随机NullReferenceExceptions更好。

您可以使用实用程序方法来清理典型的if( arg == null ) throw new ArgumentNullException(...);结构。您可能还想了解C# 4.0中的代码契约作为改进代码的一种方式。代码契约执行静态和运行时检查,可以帮助甚至在编译时识别问题。

一般来说,为方法编写前提条件是一项耗时的(因此常常被省略)实践。然而,这种防御性编码的好处在于节省了可能因未验证输入而导致的调试时间 red herrings
顺便提一下,ReSharper 是一个很好的工具,可以识别可能在运行时访问参数或本地成员为空的情况。

再给 ReSharper 加一分——我们经常使用 [NotNull] 等。 - TrueWill

1
越早越好。一旦你捕捉到(无效的)空值,就越不可能在某个关键过程中抛出异常。另一个需要考虑的事情是使用属性(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;
  }
}

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


0

取决于你的方法/ API / 库/框架类型。我认为对于私有方法来说,除非它们属于断言方法,否则不检查空值是可以的。

如果您编写的代码充满“非空”测试或“if”语句,代码将难以阅读。

定义客户端可能通过提交错误/空/ null 参数来搞砸您的代码的区域。如果您知道在哪里,则可以划定检查空值的线。就像现实生活中的安全一样,在您的 API 入口点执行此操作。

想象一下,如果每分钟都要在电影院展示您的票,那么每场电影都会很糟糕。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接