[这是一个结果最佳实践:如果函数返回null或一个空对象? 但我想是很一般。 ]
在很多传统的(嗯...生产)C ++代码,我所看到的,有写了很多 空的(或类似)进行检查,以测试指针的倾向。 许多这些得到添加空 -检查提供了一个快速解决所造成的指针引用崩溃时,附近的一个发行周期的末尾添加-并没有很多的时间进行调查。
为了解决这个问题,我就开始写了一个(代码const
)的参考参数,而不是传递指针的(多)更常见的技术。 没有指针,没有欲望检查NULL(忽略实际上有一个空引用角落的情况下)。
在C#中,相同的C ++“问题”存在:渴望核对每未知基准null
( ArgumentNullException
)和快速修复NullReferenceException
通过添加A S null
检查。
在我看来,要防止这种情况的一种方法是使用空的对象(以避免在首位空物体String.Empty
, EventArgs.Empty
)来代替。 另一个办法是抛出一个异常,而不是返回null
。
我刚开始学习F#,但它似乎存在这样的环境少得多空的对象。 因此,也许你并不真的需要有很多的null
引用左右浮动?
我在这里找错了树?
Answer 1:
我往往很可疑的,有很多空值的代码,并试图重构他们离开那里可能有例外,空集,等等。
在Martin Fowler的的“引进空对象”的格局重构 (第260页),也可能是有帮助的。 空对象响应一个真正的对象会的所有方法,但在某种程度上说,“做正确的事”。 因此,而不是随时查询订单,看是否order.getDiscountPolicy()为NULL,确保订单在这些情况下,NullDiscountPolicy。 这简化控制逻辑。
Answer 2:
传递非空,只是为了避免一个NullReferenceException是一个更加微妙,难以调试问题交易一个简单,易于解决问题(“它吹起来,因为它是空”)(“东西几个电话下来堆栈按预期的要早得多它得到了一些对象,没有有意义的信息,但不为空“)没有表现。
NullReferenceException异常是一件美妙的事情 ! 它失败硬,大声,快速,而且几乎总是快速和容易识别和修复。 这是我最喜欢的例外,因为我知道当我看到它,我的任务只是要带约2分钟。 有一个令人困惑的QA或客户报告试图描述必须被复制并追溯到起源奇怪的行为对比这一点。 呸。
这一切都归结到你,作为一个方法或一段代码,可以合理地推断哪个叫你的代码。 如果你都交给一个空引用,你可以合理地推断出什么呼叫者可以通过空意味着(也许一个空的集合,例如?),那么你绝对应该只是处理空值。 但是,如果不能合理地推断如何处理空,或者是来电者是指由空(例如,调用代码告诉你打开一个文件,并给出了位置为空)做,你应该抛出一个ArgumentNullException 。
在每一个“网关”点保持这样的正确的编码实践 - 的功能逻辑边界在你的代码 - NullReferenceException异常应该是更少见。
Answer 3:
空得到我的投票。 再说,我是“快速失败”的心态。
String.IsNullOrEmpty(......)是非常有帮助太,我猜它捕获任何一种情况:空或空字符串。 你可以写你身边掠过所有的类类似的功能。
Answer 4:
如果你正在编写的代码返回null
作为一个错误条件,那么就不要:一般来说,你应该抛出一个异常,而不是-更难错过。
如果你正在消耗你担心可能返回null,则多数情况下这些代码愚蠢的例外 :也许做一些Debug.Assert
检查,在主叫方在开发过程中感知检查的输出。 你不应该真正需要的广大 null
支票在你生产,但如果某些第三方库返回大量的null
小号不可预测的,那么肯定的是:做检查。
在4.0中,你可能想看看代码合同; 这给你更好的控制说“这种说法不应该作为null传递”,“这个功能永远不会返回空”,等等 - 并让系统验证静态分析(当你建立IE)中的索赔。
Answer 5:
关于事情null
的是,它不来的意思。 它仅仅是不存在的对象。
所以,如果你真的是一个空字符串/收集/不管,总是返回相关的对象,从来没有null
。 如果有问题的语言允许你指定,这样做。
在要返回的东西,这意味着不可指定与静态类型的值的情况下,那么你有多种选择。 返回null
是一个答案,但没有一个意思是有点危险。 抛出一个异常,实际上可能是你的意思。 您可能要延长特殊情况下的类型(可能与基因多态性,即特殊情况模式(一种特殊情况,其中是空对象模式))。 您可能希望有更多的含义来包装一个类型的返回值。 或者,你可能想在回调对象通过。 通常有很多选择。
Answer 6:
我会说这要看情况。 对于返回单个对象的方法,我通常返回null。 对于返回集合的方法,我通常会返回一个空的集合(非空)。 这是一起比规则指引的线路多,虽然。
Answer 7:
如果你是认真的“要计划null
少”的环境下,可以考虑使用扩展方法更多的时候,他们是免疫的NullReferenceException异常和至少“假装”那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贝特朗·梅耶的“创始人”认为null
恶从他的面向对象设计,并应用于艾菲尔语言彻底放逐,但那是另一回事)。 编辑:丹提到比尔·瓦格纳( 更有效的C#)认为不好的做法,他是正确的。 曾经被认为是IsNull
扩展方法;-)?
为了使你的代码更易读,另一条线索可能在的地方:使用空合并运算更经常地指定一个默认当对象为空:
// load settings
WriteSettings(currentUser.Settings ?? new Settings());
// example of some readonly property
public string DisplayName
{
get
{
return (currentUser ?? User.Guest).DisplayName
}
}
这些都不需要偶尔检查null
远(和??
什么更多的则隐若分支)。 我宁愿少null
在我的代码有可能,仅仅是因为我相信,这使代码更易读。 当我的代码被用if语句用于堆满null
的,我知道有一些错误在设计和重构我。 我建议任何人这样做,但我知道,意见对此事的变化很大。
(更新)有例外的比较
在讨论中没有提到,到目前为止是异常处理的相似性。 当你发现自己忽略无处不null
当你认为它在你的方式,它基本上是一样的文字:
try
{
//...code here...
}
catch (Exception) {}
它具有消除例外的任何痕迹的效果才发现它的代码更晚引发无关例外。 虽然我认为这是很好的避免使用null
,因为在这个线程前面所提到的,对于特殊情况下具有零是好的。 只是不要把他们藏在空忽略块,它最终将不得不为捕获所有的例外块相同的效果。
Answer 8:
对于例外的主角,他们通常从事务性的编程和强大的异常安全性保证或盲准则干。 在任何像样的复杂性,即。 异步工作流程,I / O和特别是网络代码,它们仅仅是不合适的。 为什么你看到在C ++此事谷歌风格文件的原因所在,以及一切美好的异步代码“不执行它(想想你最喜欢的管理池为好)。
还有更多的它,虽然它可能看起来像一个简化,它真的就是这么简单。 对于一个你会得到很多的东西例外,这不是专为重型异常使用..反正我离题了,在这个世界顶级设计师图书馆阅读时,通常的地方是升压(只是不混合起来在升压对方阵营,喜欢的例外,因为他们不得不写音乐软件:-)。
在您的实例,这是不是福勒的专业技术,高效的“空对象”成语只能在C ++由于现有铸造机制(或许可以,但肯定不是总是显性的方式)..在疗法另一方面,在空类型你有能力抛出异常,做任何你想要的,同时保持代码的干净调用位置和结构。
在C#中您的选择可以是一个类型要么是好还是畸形的一个实例; 因此它是能够投掷acceptions或简单地运行原样的。 因此,它可能会或可能不会违反其他合同(取决于你认为什么是更好的根据的代码,你所遇到的质量)。
最后,它并清理调用点,但不要忘记,你将面临许多图书馆的冲突(特别是从容器/字典返回,结束迭代器映入脑海,和任何其他“”接口代码到外面的世界)。 加空的值检查极其优化的机器代码,一些作品要记住,但我会同意的任何一天野指针的使用不理解常量性,参考文献和更多的会导致不同种类的可变性,别名和PERF问题。
要添加,没有银弹,并拍击空引用或管理的空间使用空引用,或投掷,而不是处理异常是一个相同的问题,尽管什么管理和异常的世界将尝试向你推销。 任何像样的环境中提供从这些保护(哎呀,你可以在任何你想要的操作系统上安装任何过滤器,还有什么你觉得虚拟机做的),还有许多其他的攻击向量,这其中已经overhammered碎片。 再次输入来自谷歌的x86验证,自己做更快的方式,更好地“IL”,“动态”友好代码等。
用你的直觉这一点,体重利弊去本地化的影响。在未来你的编译器将优化所有的检查无论如何,远远超过任何运行时更有效地或编译时人的方法(但不容易跨-module交互)。
Answer 9:
我会尽量避免从一个方法返回null尽可能。 通常有两种情况 - 当空的结果将是合法的,当它不应该发生。
在第一种情况下,如果没有结果是合法的,也有可用来避免空结果和与它们相关的无效支票几种解决方案: 空对象模式和特殊情况的模式在那里返回什么都不做替换对象,或者做一些具体的在特定情况下的事情。
如果是合法的,没有返回的对象,但还是有在空对象或特殊情况方面没有合适的替代,那么我通常使用选项功能型 -那么我可以返回一个空的选项时,没有法律的结果。 然后由该客户端,看看有什么是应对空期权的最佳方式。
最后,如果它是不合法的已经从一个方法返回的任何对象,只是因为如果事情是缺少方法不能产生它的结果,那么,我选择抛出一个异常,减少进一步执行。
Answer 10:
你不能总是返回一个空的对象,因为“空”并不总是定义。 例如这是什么意思为int,float或布尔是空的?
返回NULL指针不一定是不好的做法,但我认为这是一个更好的做法是返回一个(常量)引用(它是有道理这样做当然)。
而最近我经常使用的易错的类:
Fallible<std::string> theName = obj.getName();
if (theName)
{
// ...
}
有许多可用于这样一类的各种实现(查看谷歌代码搜索), 我也创建了自己 。
Answer 11:
怎么是空的对象不是空的对象比较好 ? 你只是重命名症状。 问题是,你的函数的合同过于松散定义“这个功能可能会返回一些有用的东西,或者它可能会返回一个空值”(其中的虚值可能为空,“空对象”,或幻常量一样-1
。但不管你如何表达这个虚拟值,主叫还是要检查它,他们使用的返回值前。
如果你想清理你的代码,该解决方案应该是缩小功能,使得它不会在第一时间返回一个空值。
如果您有可能会返回一个值,或可能会返回一个什么功能,然后指针来表达这个共同的(合法的)的方式。 但往往,你的代码可以进行重构,使这种不确定性被删除。 如果你能保证一个函数返回一些有意义的东西,然后呼叫者可以依靠它返回一些有意义的事情,然后他们没有检查返回值。
文章来源: null objects vs. empty objects