【问题标题】:When to check for nulls何时检查空值
【发布时间】:2011-04-26 22:57:20
【问题描述】:

这是一个开放式问题,但我正在尝试通过异常处理方面的良好实践来提高我的技能,特别是在一般情况下检查空值时。

我主要知道何时检查空值,但我不得不说一半的时间我不知道,这让我很困扰。我知道除非您使用可为空的 int,否则不能将 int 设置为 null。我知道字符串可以设置为 null 或空,因此您可以检查 IsNullOrEmpty。

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

但这里还有更多关于异常处理的内容。例如,我不知道何时总是在代码中明确检查并抛出空引用异常。如果我有传入的参数,通常很简单,但有时我会问自己,是否需要显式的 null 抛出?

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

【问题讨论】:

    标签: c# exception-handling


    【解决方案1】:

    你不应该抛出NullReferenceException。如果它是 null 的参数,则改为抛出 ArgumentNullException

    我更喜欢检查公共/受保护方法的所有引用类型参数是否为 null。对于私有方法,如果您确定所有调用将始终具有有效数据,则可以省略检查。

    【讨论】:

    • 同样,我总是检查可以被我没有编写的代码访问的方法的参数上的null,并抛出一个ArgumentNullException。 (当然,除非null 是一个有效的参数。)如果我没有,调用者会得到一个NullReferenceException,这基本上意味着'你调用的代码是一团糟'。对于私有代码,我宁愿使用Debug.Assert 而不是抛出。它在调试版本中具有相同的错误报告能力,并且在发布版本中永远不会发生错误。
    • 我从不检查,除非我将参数存储在成员中(如果我要创建公共 API,我也会这样做)。说真的,当一切都在同一个解决方案中时,你得到 NullReferenceException(= 方法内没有额外的代码)还是 ArgumentNullException(= 方法内的额外代码)是否重要?
    • 如果你正在检查 IsNullOrEmpty,你应该抛出什么样的异常?
    • @coffeeaddict:对于 String.IsNullOrEmpty,我会抛出相同的 ArgumentNullException。在此处查看相关问题:stackoverflow.com/questions/1355957
    • @Bobby 如果您确定它不会为空,为什么会产生开销。例如。公共方法检查然后调用私有实现。为什么还要检查?你可以推理私有方法的使用。
    【解决方案2】:

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

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

    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(除了可能应对第三方的不良行为)。

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

    【讨论】:

    • 值得一提的是,.NET 代码契约的一大好处是它们能够提供一定级别的编译时(静态)分析,以识别可能将 null 传递给方法的情况。在编译时发现问题总是比在运行时更好。
    • @LBushkin:只要你有 Team System,当然……(或者学术许可证。)
    • 是的。但是现在谁不使用团队系统呢?我的意思是每个座位只有 500,000 美元或类似的东西,你知道的。但希望这个功能最终会成为 Visual Studio 的核心。
    • .NET Framework 4 中的新功能:msdn.microsoft.com/en-us/library/dd409230(VS.100).aspx 他们列出了代码协定 :)
    • @Joren:是的,代码合同在 .NET 4.0 中(有点;核心是),但静态检查器仅限于团队系统用户。
    【解决方案3】:

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

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

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

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

    一般来说,为方法编写前置条件是一种耗时(因此经常被省略)的做法。但是,这种类型的防御性编码的好处在于可以节省数小时的调试时间red herrings,因为您没有验证您的输入。

    附带说明,ReSharper 是一个很好的工具,可以识别对在运行时可能为空的参数或本地成员的潜在访问。

    【讨论】:

    • ReSharper 的另一个 +1 - 我们使用 [NotNull] 等等。
    【解决方案4】:

    越早越好。您越早捕获(无效)空值,您就越不可能在某些关键过程中被丢弃。要考虑的另一件事是使用您的属性(设置器)来集中您的验证。我经常在构造函数中引用我的属性,因此我可以很好地重用该验证。

    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;
      }
    }
    

    【讨论】:

      【解决方案5】:

      取决于您的方法/api/库/框架类型。我认为私有方法可以,除非它们是一种断言方法,否则它们不检查空值。

      如果您编写防御性程序并在代码中乱扔“非空”测试或“if”语句,您的代码将不可读。

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

      想象一下,你每分钟都会被打扰在电影院出示你的票,每部电影都会很糟糕。

      【讨论】:

        【解决方案6】:

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

        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

        【讨论】:

          猜你喜欢
          • 2011-06-03
          • 1970-01-01
          • 2021-10-12
          • 2020-07-22
          • 1970-01-01
          • 2013-05-16
          • 2014-05-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多