为什么尝试访问null属性会导致某些语言出现异常?

时间:2011-08-08 15:09:38

标签: c# javascript null nullreferenceexception

对于某些编程语言(例如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

如果foobarbaz中的任何一个为空,我的代码就会被破坏。 :(此外,如果我在控制台中评估以下表达式,似乎有不一致的结果:

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.

有什么我想念的吗? 为什么这些语言不会使用此行为?由于某种原因难以实施吗?它会导致我忽略的问题吗?

5 个答案:

答案 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”类以产生您最终得到的结果 - 干净的代码。