代码之家  ›  专栏  ›  技术社区  ›  Ðаn

空对象与空对象

  •  13
  • Ðаn  · 技术社区  · 15 年前

    [这是由于 Best Practice: Should functions return null or an empty object?

    很多 无效的 (或类似)检查测试指针。当添加一个 无效的

    为了解决这个问题,我开始编写代码,花了很长时间( const 无效的 (忽略实际具有空引用的情况)。

    在C++中,存在同样的C++“问题”:对每个未知引用进行检查的愿望。 null ( ArgumentNullException NullReferenceException 通过添加 检查。

    String.Empty , EventArgs.Empty 无效的 .

    我刚刚开始学习F#,但在那个环境中似乎空对象要少得多。所以也许你真的不需要有很多 到处都是参考资料?

    我找错树了吗?

    11 回复  |  直到 8 年前
        1
  •  7
  •   Jim Ferrans    5 年前

    我倾向于对有大量空值的代码持怀疑态度,并尝试在可能的情况下对它们进行重构,包括异常、空集合、Java选项等等。

    Martin Fowler的“引入空对象”模式 Refactoring (第260页)也可能有帮助。空对象响应真实对象将响应的所有方法,但以“做正确的事情”的方式响应。因此,与其总是检查订单以查看Order.getDiscountPolicy()是否为NULL,不如确保订单在这些情况下具有NullDiscountPolicy。这简化了控制逻辑。

        2
  •  30
  •   Ðаn    4 年前

    NullReferenceException 用一个简单易解的问题(“它爆炸了,因为它是空的”)换成了一个更微妙、更难调试的问题(“堆栈中多次调用的某个对象的行为不符合预期,因为它很早就得到了一些没有有意义的信息但不是空的对象”)。

    是一个 好极了

    这一切都归结为,作为一种方法或一段代码,您可以合理地推断出调用您的代码。如果您收到一个null引用,并且您可以合理地推断调用方对null的含义(例如,可能是一个空集合),那么您肯定应该只处理null。但是,如果您不能合理地推断如何处理null,或者调用方使用null表示什么(例如,调用代码告诉您打开一个文件并将位置指定为null),则应该抛出 ArgumentNullException .

    NullReferenceExceptions 应该更加罕见。

        3
  •  5
  •   Ðаn    4 年前

    String.IsNullOrEmpty(...)

        4
  •  3
  •   Marc Gravell    15 年前

    如果您正在编写返回 null 作为一种错误情况,那么就不要这样做:一般来说,您应该抛出一个异常,而不是一个更难忽略的异常。

    如果您使用的代码担心返回null,那么这些代码主要是 boneheaded exceptions 字体也许做一些 Debug.Assert 真正地 需要 数量 无效的 检查您的产品,但如果某些第三方库返回大量 无效的 It’这是不可预测的,那么一定要做检查。

    在4.0中,您可能希望查看代码契约;这使您能够更好地控制“此参数永远不应作为null传入”、“此函数永远不返回null”等,并让系统在静态分析期间(即在构建时)验证这些声明。

        5
  •  2
  •   Tom Hawtin - tackline    15 年前

    关于 null

    因此,如果您真的是指空字符串/集合/任何内容,请始终返回相关对象,而不要返回 无效的 . 如果所讨论的语言允许您指定,请这样做。

    如果您想要返回一些表示不能用静态类型指定的值的内容,那么您有许多选项。返回 无效的

        6
  •  1
  •   tvanfosson    15 年前

    我认为这要视情况而定。对于返回单个对象的方法,我通常会返回null。对于返回集合的方法,我通常会返回一个空集合(非null)。不过,这些规则更多的是指导方针,而不是规则。

        7
  •  1
  •   Community CDub    5 年前

    null 无效的

    public static GetExtension(this string s)
    {
        return (new FileInfo(s ?? "")).Extension;
    }
    

    可称为:

    // this code will never throw, not even when somePath becomes null
    string somePath = GetDataFromElseWhereCanBeNull();
    textBoxExtension.Text = somePath.GetExtension(); 
    

    我知道,这只是方便,很多人正确地认为它违反了OO原则(尽管OO,Bertrand Meyer的创始人)认为 无效的 更有效的C# )认为这是一种坏习惯,他是对的。有没有考虑过 IsNull 扩展方法;-)?

    为了使代码更具可读性,可能还有另一个提示:当对象为null时,更频繁地使用null合并运算符来指定默认值:

    // load settings
    WriteSettings(currentUser.Settings ?? new Settings());
    
    // example of some readonly property
    public string DisplayName
    {
        get 
        {
             return (currentUser ?? User.Guest).DisplayName
        }
    }
    

    这些都不需要偶尔检查一下 无效的 ?? 无效的 在我的代码中尽可能多地使用,只是因为我相信它使代码更具可读性。当我的代码被 无效的 ,我知道设计中有问题,我进行了重构。我建议任何人也这样做,但我知道在这个问题上意见分歧很大。

    (更新)与例外情况的比较

    到目前为止,讨论中没有提到异常处理的相似性。当你发现自己到处被忽视的时候 无效的

    try 
    {
        //...code here...
    }
    catch (Exception) {}
    

    这样做的效果是,删除任何异常跟踪后,发现它在代码的后面会引发不相关的异常。虽然我认为避免使用很好 无效的 例外情况 这很好。只是不要将它们隐藏在null ignore块中,它最终将产生与catch all exceptions块相同的效果。

        8
  •  1
  •   rama-jka toti    15 年前

    对于异常主角来说,它们通常源于事务性编程和强大的异常安全保证或盲目指导。在任何相当复杂的情况下,例如异步工作流、I/O,尤其是网络代码,它们都是不合适的。为什么你在C++中看到谷歌风格文档,以及所有优秀的异步代码“不执行它”(也想想你最喜欢的管理池)。

    它有更多的含义,虽然看起来像是一种简化,但实际上就是这么简单。首先,在一些不是为大量异常使用而设计的东西中,你会遇到很多异常。。无论如何,我离题了,从世界顶级图书馆设计师那里读到这篇文章,通常的地方是boost(只是不要把它与boost中喜欢例外的其他阵营混淆,因为他们必须编写音乐软件:-)。

    支配地位 ).. 另一方面,在你的空 类型

    在C#中,您可以选择一个类型良好或格式错误的单一实例;因此,它能够抛出接受或简单地按原样运行。因此,它可能会也可能不会违反其他契约(取决于您所面临的代码质量,您认为什么更好取决于您自己)。

    另外,这里没有银弹,不管托管和异常世界会向您推销什么,在托管空间中崩溃空引用或使用空引用,或者抛出异常而不处理异常都是一个相同的问题。任何像样的环境都可以提供保护(见鬼,你可以在你想要的任何操作系统上安装任何过滤器,你认为虚拟机还能做什么),而且还有太多其他攻击向量,这一个已经被彻底改造成碎片。再次进入谷歌的x86验证,这是他们自己做得更快更好的“IL”、“动态”友好代码等的方式。。

    按照你的直觉去做,权衡利弊并定位影响。。将来,您的编译器将优化所有这些检查,并且比任何运行时或编译时人工方法都要高效(但对于跨模块交互来说就不那么容易了)。

        9
  •  1
  •   Zoran Horvat    10 年前

    在第一种情况下,当没有合法结果时,有几种解决方案可用于避免空结果和与之相关的空检查: Null Object pattern Special Case pattern 返回不做任何事情或在特定情况下做特定事情的替换对象。

    如果不返回对象是合法的,但仍然没有空对象或特殊情况的合适替代品,那么我通常使用 Option functional type -当没有合法结果时,我可以返回一个空选项。然后,由客户来确定处理空选项的最佳方法。

        10
  •  1
  •   Ðаn    4 年前

    空对象是什么 更好的 而不是空对象?你只是在重新命名症状。问题是函数的契约定义过于松散,“此函数可能返回有用的内容,或者可能返回一个伪值”(其中伪值可能为null、“空对象”或类似的神奇常量) -1 )但无论您如何表示此虚拟值,调用方 仍然 在使用返回值之前,必须检查它。

    如果您想清理代码,解决方案应该是缩小函数的范围,这样它就不会首先返回一个伪值。

    如果您有一个函数,它可能返回一个值,或者可能什么也不返回,那么指针是一种常见的(有效的)表达方式。但通常,您的代码可以进行重构,以消除这种不确定性。如果您可以保证函数返回有意义的内容,那么调用方就可以依靠它返回有意义的内容,而不必检查返回值。

        11
  •  0
  •   StackedCrooked    15 年前

    您不能总是返回空对象,因为“empty”并不总是定义的。例如,int、float或bool为空意味着什么?

    Fallible<std::string> theName = obj.getName();
    if (theName)
    {
        // ...
    }
    

    此类类有多种实现(请查看Google代码搜索), I also created my own