对于某些编程语言(例如C#,Javascript)而言,最让我困扰的是,尝试访问null
的属性会导致错误或异常发生。
例如,在以下代码段中,
foo = bar.baz;
如果bar是null
,C#会抛出讨厌的NullReferenceException
,我的Javascript解释器会抱怨Unable to get value of the property 'baz': object is null or undefined
。
理论上我可以理解这一点,但在实际代码中,我经常有一些深层对象,比如
foo.bar.baz.qux
如果foo
,bar
或baz
中的任何一个为空,我的代码就会被破坏。 :(此外,如果我在控制台中评估以下表达式,似乎有不一致的结果:
true.toString() //evaluates to "true"
false.toString() //evaluates to "false"
null.toString() //should evaluate to "null", but interpreter spits in your face instead
我绝对鄙视编写代码来处理这个问题,因为它总是冗长,有臭味的代码。以下不是人为的例子,我从我的一个项目中抓取了这些(第一个是Javascript,第二个是C#):
if (!(solvedPuzzles &&
solvedPuzzles[difficulty] &&
solvedPuzzles[difficulty][index])) {
return undefined;
}
return solvedPuzzles[difficulty][index].star
和
if (context != null &&
context.Request != null &&
context.Request.Cookies != null &&
context.Request.Cookies["SessionID"] != null)
{
SessionID = context.Request.Cookies["SessionID"].Value;
}
else
{
SessionID = null;
}
如果整个表达式返回null
,如果其中任何一个属性为null,事情会变得容易得多。上面的代码示例可以简单得多:
return solvedPuzzles[difficulty][index].star;
//Will return null if solvedPuzzles, difficulty, index, or star is null.
SessionID = context.Request.Cookies["SessionID"].Value;
//SessionID will be null if the context, Request, Cookies,
//Cookies["SessionID"], or Value is null.
有什么我想念的吗? 为什么这些语言不会使用此行为?由于某种原因难以实施吗?它会导致我忽略的问题吗?
答案 0 :(得分:8)
它会导致我忽略的问题吗?
是的 - 这会导致您期望的问题存在非空值,但由于存在错误,您将获得空值。在那种情况下你想要一个例外。静默失败并坚持使用糟糕的数据是一个非常糟糕的主意。
某些语言(例如Groovy)提供了一个可以帮助的空安全解除引用运算符。在C#中,它可能看起来像:
SessionID = context?.Request?.Cookies?["SessionID"]?.Value;
我相信C#团队过去曾考虑过这个问题并发现它有问题,但这并不意味着他们将来不会重新访问。
答案 1 :(得分:2)
然后如果尾部是对一个返回bool的方法的调用,那么呢?
Main.Child.Grandchild.IsEdit()
和bool IsEdit();
我认为最好有一个返回默认行为的“null”实例(这称为the null object pattern)。
这样,对于那些期望null表示问题的人,他们会得到他们的异常,但是如果你知道默认对象是可以接受的,你可以实现它而不用担心它。然后解决这两种情况。
您可以从INull派生所有“null”对象,将它们放入与其类名称相关的哈希值,然后在成员为空时引用它。然后,您可以控制默认的“null”实现(如果对象为“null”,则可以接受)
此外,“null”对象可以在访问异常时抛出异常,这样就知道您访问了异常,并选择何时可以。所以你可以实现自己的“空对象异常”。
如果他们为此提供任何语言支持,那么它可以是每个人,全面翻译单元,也可以是具有限定符的对象(最后一个是更好的选项),此时它不会默认情况下,您可能已经实现了默认的“null”实例。
答案 2 :(得分:1)
我可能会建议,如果你必须检查那么多空值,那么你的代码编写得很糟糕。显然,由于我没有你的代码,我不能说得最终。我建议你将代码分成更小的函数。这样,您的代码一次只需要检查一个或两个空值,并且可以更好地控制在任何时候某些内容为空时会发生什么。
答案 3 :(得分:0)
一般来说,foo.Baz的意思是“在实例上如果foo,执行baz成员”。这可能是一种动态语言,虚拟成员或任何东西 - 但我们在前提的第一步就失败了:没有实例。
您的边缘是一个空安全的成员访问运算符。非常罕见,坦率地说,我不同意你声称这会产生臭臭的代码(尽管我可以说你在一个表达式中查询太多级别以保持健康)。
同样,我不同意null.toString()
应该返回“null” - IMO对于此失败是完全正常的。将对null的访问视为“正常”本质上是错误的IMO。
答案 4 :(得分:0)
有什么我想念的吗?为什么这些语言不使用它 行为反而?由于某种原因难以实施吗?会吗 造成我忽视的问题?
当变量为null
时,如果不期望,您提及的语言选择提前失败。这是创建正确工作程序时的核心概念。
选项是隐藏空指针问题,程序会再运行一行,但可能会在以后崩溃甚至更糟,导致输出错误!
此外,当尝试修复错误时,如果它立即抛出NullReferenceException
并且调试器显示开始查看的行,则更容易修复它。替代方案是模糊的症状 - 更难调试!
当你不应该使用null
进行编程是很常见的。在上面的考试中,null
意味着什么。答案是,它没有任何意义。作为代码的读者,我会假设作者忘了一些东西。
通常最好的解决方案是引入一个小的,我称之为“null对象”,而不是null
。在您的示例中,可能会返回“NoDifficulty”或“UnknownDifficulty”类以产生您最终得到的结果 - 干净的代码。