【问题标题】:Where to check if an object is null or not?在哪里检查对象是否为空?
【发布时间】:2010-10-16 21:59:09
【问题描述】:

在哪里检查传递给方法的对象是否为空?

在调用方法之前是否需要测试对象?还是在使用参数的方法中?

public class Program
{
    public static void Main(string[] args)
    {
        // Check if person is null here? or within PrintAge?

        PrintAge(new Person { Age = 1 });
    }

    private static void PrintAge(Person person)
    {
        // check if person is null here?

        Console.WriteLine("Age = {0}", person.Age);
    }
}

public class Person
{
    public int Age { get; set; }
}

在两个类中进行“null”检查似乎是多余的代码。

[编辑]:在调用者或被调用者中检查 null 有什么缺点/优点?

[EDIT2]:我刚遇到Defensive Programming,它似乎提倡在被调用者中检查 null。我想知道这是否是一种被广泛接受的做法。

【问题讨论】:

标签: .net null


【解决方案1】:

您的意思是同时检查这两种方法?我肯定会检查 PrintAge,如果它在 Main 中也有意义。我认为一般来说没有明确的答案。这取决于:-)

【讨论】:

    【解决方案2】:

    我会说检查它 n PrintAge 似乎更有意义,因为这可以履行例程的合同。当然,您可以将空检查替换为 Debug.Assert() 代码以在测试时进行检查,而不是在发布时进行检查。

    【讨论】:

      【解决方案3】:

      如果实例为空,你想做什么?

      我认为这取决于您提供的 API 和定义合同(.net 框架类的方式)。话虽如此,如果方法定义了在传递给它的 null 引用的情况下的预期结果,则无需检查 null(在 main 中)。

      【讨论】:

      • 实际上,当对象为空时,我应该做什么取决于很多情况。如果我在被调用者中检查 null,那么我会抛出异常或什么都不做。在被调用的函数中,我也会这样做。
      • 我的意思是假设它是一个你让外部用户调用的方法,如果传递的值为空,你想在被调用的方法中做什么?
      • @shahkalpesh:我有时会想抛出一个异常或只返回一个空列表,具体取决于方法的作用(是否是 Get/Set 方法)
      【解决方案4】:

      我通常让我的空检查由我的期望控制;如果我希望某些内容为空或不确定,我会添加一张支票。否则我不会。空指针异常是最容易跟踪的问题之一,因此过多的检查会使代码膨胀。在具体示例中,我什么都不检查,因为它很直观,它不为空。

      【讨论】:

      • 所以基本上,你的做法是这样的,根据现在的情况而变化?
      • 是的。我试图表达我的期望,即使是通过这样的检查。
      【解决方案5】:

      冗余代码不是最优雅的,而是最安全的。

      这取决于您的目标用户是谁,如果是您,那么您可以控制所有内容的使用方式,并且只有在您不确定变量的状态是什么时才需要进行检查。

      如果您将其制作给其他人使用,那么空值检查可能是一个好主意。即使你只是抛出一个 NullPointerException 也最好快速失败。

      【讨论】:

        【解决方案6】:

        构造函数只有一种情况可以返回 null [new() on a Nullable<T>] - 所以调用代码不需要检查。

        被调用者可能应该检查;如果为空,则抛出 ArgumentNullException。在 .NET 4.0 中,代码合同将更好地满足这一点。但还没有;-p

        【讨论】:

        • 我仍然无法理解的是“为什么”“调用代码不需要检查”并将空检查转储到被调用方。
        • 因为它不可能(使用最初呈现的代码)为空。
        【解决方案7】:

        您在Main 中没有要检查的内容 - 您正在使用永远不会返回 null 的 new 运算符(Nullable<T> 除外)。

        签入PrintAge 是完全合理的,尤其是在它被公开的情况下。 (对于私有 API,进行参数检查不太重要,但它仍然非常有用。)

        if (person == null)
        {
            throw new ArgumentNullException("person");
        }
        

        这些天在 C# 3.0 中我通常use an extension method for this

        【讨论】:

          【解决方案8】:

          您可以设计一种仅使用有效对象的方法。

          这意味着您应该收到有效的对象(在您的情况下不是 null )。
          这意味着您不知道如何反应以及如何处理无效对象:

          • 从函数中静默返回 不是解决方案;
          • 抛出异常意味着你将责任转移到上层 他们可以在传递给您之前检查值的方法。

          因此,如果您的方法不确切知道如何处理无效对象,并且该方法在无效情况下不会遵循其他逻辑,您应该放置

          Debug.Assert( Person );
          

          PrintAge 开始,这将强制您通过调用堆栈进行检查

          层次结构中较低的功能是它应该做的检查较少。以下是在执行工作的函数中进行检查的缺点

          • 做实际工作的函数 必须尽可能清楚 没有大量的ifs
          • 函数将被调用多次
          • 此类函数可以调用此类函数,并且它们可以再次调用此类函数。他们每个人都将执行相同的验证

          【讨论】:

          • Debug.Assert 的问题在于,除非您在发布版本中明确包含 DEBUG 符号,否则您的代码在开发和生产中的行为会有所不同——不好。生产是您想要真正确保您不会继续使用不良数据的时间 :)
          • Assert in Debug 将强制您在调用此 PrintAge 之前进行检查
          • +标记为答案:“他们每个人都将执行相同的验证”
          • 如果此代码不受单元测试支持,并且如果它位于流程的某个模糊部分,您可能不会发现它,并且 Assert 的错误安全性可能会让您反击!
          • @Slough:Assert 的虚假安全性?这是什么意思?您不控制项目的构建方式吗?单元测试。是否需要放置所有代码交叉单元测试?我厌倦了所有这些“如果以防万一”,这是由于较低级别函数中的一些“如果”而开始的。
          【解决方案9】:

          一定要签到PrintAge,这是一个正确的检查地点。它可能是多余的,但不会伤害任何人,除非您每秒执行 1000 次。 (根据检查抛出异常或修复它,如果可以的话)

          其他检查取决于您的实际流程,在此示例中您没有流程,因此我无法对此发表评论。但通常认为你的参数被污染了。

          【讨论】:

          • 为什么在“PrintAge”中检查可空性是“正确的检查位置”?我最初担心到处都是冗余代码......
          • 我认为这是正确的地方,因为如果您从其他地方调用 PrintAge,它仍然可以正常运行(或至少更好)。
          • 但是很明显,当你有多个 PrintAge 类似的功能时,它会导致冗余,而且没有太多的事情要做。
          【解决方案10】:

          如果你设计一个库,就会有一些方法暴露给外部世界。您应该检查此方法中的传入数据。在您不公开的方法中不需要检查,因为只有您的代码调用它们,并且它的逻辑应该处理您在调用的公开方法中接受的所有情况。

                              --------------------------
                             |                          |
                             |         Library          |
                             |                          |
           -------        ---------        ----------   |
          |       |      |         |      |          |  |
          | Outer |      | Library |      | Library  |  |
          |       | ===> | Entry   | ===> | Backend/ |  |
          | World |      | Method  |      | Helpers  |  |
          |       |      |         |      |          |  |
           -------        ---------        ----------   |
                             |                          |
                             |                          |
                              --------------------------
          

          如果您已经接受了入口方法中提供的数据,您应该执行请求的操作并返回预期的结果,即处理所有剩余的情况。

          更新

          澄清图书馆内的情况。可能有空检查,但只是因为逻辑,而不是因为参数验证。库中空检查的位置有两种可能性。第一个,如果被调用的方法知道如何处理空值。

          private CallingMethod()
          {
             CalledMethod(someData);
          }
          
          private CalledMethod(Object parameter)
          {
             if (parameter == null)
             {
                // Do something
             }
             else
             {
                // Do something else
             }
          }
          

          第二种情况,如果你调用一个不能处理空值的方法。

          private CallingMethod()
          {
             if (someData == null)
             {
                // Do the work myself or call another method
             }
             else
             {
                CalledMethod(someData);
             }
          }
          
          private CalledMethod(Object parameter)
          {
             // Do something
          }
          

          整个想法是拒绝您无法立即处理的案件并妥善处理所有个剩余案件。如果输入无效,则抛出异常。这会强制库调用者仅提供有效值,并且不允许调用者以无意义的返回值继续执行(除了调用者将异常浅化并继续)。

          【讨论】:

          • 那么在“库后端/助手”中,调用者和被调用者都没有“空”检查?我是否正确理解您的观点?
          • 可能会有空检查,但前提是实际逻辑需要。没有空检查只是为了验证参数。
          【解决方案11】:

          PrintAge 应该是 Person 上的一个方法,而不是一个以 Person 作为参数的静态方法。无需检查。

          检查空值会使代码变得不必要地复杂。构建您的代码以限制(或消除)可能为 null 的情况,这样您要编写的检查就会少得多。

          【讨论】:

          • “检查空值会使代码变得不必要的复杂”
          • 两个地方。我的偏好是完全不需要检查。
          【解决方案12】:

          据我了解,您的问题比您的示例说明的更为笼统。我的偏好如下:

          • 所有可公开访问的方法都必须检查 NULL 输入并酌情抛出异常。因此,如果您正在构建一个供他人使用的框架,请进行防御性编码。
          • 如果您知道这是在其他地方完成的或者参数永远不会为 NULL,则私有方法可能会省略 NULL 检查,但总的来说,我更喜欢显式 ArgumentNullException 而不是 NullRefereceException。

          Brad Abrams 在这里有更多意见:http://blogs.msdn.com/brada/archive/2004/07/11/180315.aspx

          【讨论】:

          • @Brian:是的,我的问题有点太笼统了。
          【解决方案13】:

          我更喜欢方法内部的空检查,原因有两个。

          1. 我认为函数应该是“完整的”,即处理空值/“边缘情况”,而不是依赖调用者。这有两个原因,

            • 稍后调用该方法的人可能会忘记添加空检查
            • 在单元测试中使用边缘情况测试方法更容易。
          2. 在方法内部进行空检查会减少代码中空检查的总数,这通常意味着代码的可读性更高

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-11
            • 2014-01-01
            • 2017-08-11
            相关资源
            最近更新 更多