我有一些开发人员不断提出If null检查
例如:
Run(Order order)
{
if (order == null) return;
}
在他们的代码中,因为他们认为如果有人传入一个null参数,他们就会保护他们的类。我试图告诉他们他们的逻辑中的缺陷,因为如果有人在这种情况下传入null,它很可能是消费者代码的问题,而不是这个类抛出异常并且快速失败,它优雅地处理了不良行为消费者,并继续努力。
另一个建议是让前提条件或防护类快速失败并抛出异常。任何事情,但忽略了消费者可能有一些其他问题的事实,我帮助掩盖它。
我如何让人们欣赏你的班级不应该如此宽容的事实。如果有人没有传递好的数据,应该告诉他们。
任何好的文章或建议可以帮助我理解这一点吗?
答案 0 :(得分:13)
如果你的班级不能接受null
论证,那么最好的办法是:
if (arg == null)
throw new ArgumentNullException();
这比在堆栈中深入NullPointerException
更为可取。在最糟糕的情况下,您将在某处缓存null
,并且直到很久之后才会实际触发异常,并且看看您将调试问题然后有多么有趣。
正如其他人所说,有时候合同说null
没问题。在这种情况下,在代码的某些部分附近有一个保护条款是正确的 - 尽管那时候我会说最好的设计是添加一个没有可选null参数的重载。
答案 1 :(得分:7)
这实际上取决于具体情况。正如您似乎指出的那样,很少提供诸如“不在代码中放置空检查”之类的一般性建议。班级合同应该定义什么是合法的,什么不是。但是,如果合同明确表明传入null是不可接受的,那么例外确实是一种恰当的反应。
答案 2 :(得分:3)
正如其他人所说的那样,早期失败比在生产中遇到神秘问题更为可取,因为功能在预期时没有做任何事情。如果函数返回null参数,如示例所示。)
即使函数没有返回并只抛出一个NullReferenceException
,当你知道一个参数为null时,它也可以解决一个bug。如果函数抛出NullReferenceException
,则您不知道null
是什么或它的错误。
我想补充说ArgumentNullException
有一个参数是有原因的。
最好写
if(myArg == null) throw new ArgumentNullException("myArg");
而不是ArgumentNullException
而不是paramName
。
这样,如果你有一个带有五个参数的函数的异常,你就会知道哪个参数导致了这个问题。如果您无法附加调试器,这一点尤为重要。 (例如,在生产Web服务器或最终用户计算机上)
如果您正在编写许多函数,这可能会产生大量开销,尤其是因为字符串没有IntelliSense。我编写了一个代码片段来生成这些检查:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Check for null arguments</Title>
<Shortcut>tna</Shortcut>
<Description>Code snippet for throw new ArgumentNullException</Description>
<Author>SLaks</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>Parameter</ID>
<ToolTip>Paremeter to check for null</ToolTip>
<Default>value</Default>
</Literal>
</Declarations>
<Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$");
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
答案 3 :(得分:2)
我没有看过它,但是eiffel.com has 2 presentations(幻灯片+音频)关于按合同设计的主题。这些家伙有invented这个概念,所以如果有人能够解释它,那就是: - )
答案 4 :(得分:2)
.net 4.0中的代码合同有望使这种行为更加一致。 任何谈论代码合同的文章都有助于实现这个想法,将来,这种语法将提供方法。
答案 5 :(得分:1)
有时你无法告诉人们为什么这样的练习是错误的 - 他们必须自己解决。但是你可以通过提出一些单元测试来帮助他们到达那里,这会导致由于这个问题导致一些令人讨厌的失败,并让他们调试错误。
答案 6 :(得分:1)
如果方法的契约指定其参数不应为null,那么正确的做法是通过使用Assert使其显式化,如下所示:
Debug.Assert( item != null, "Null items are not supported );
使用调试配置构建可执行文件时,这将快速失败,但使用版本配置构建时,性能会降低。
答案 7 :(得分:1)
这似乎是一个关于如何最好地编写可管理代码的问题。我的新信念是你必须对代码的所有消费者都不了解。假设我或者有深度知识的人会消耗我的代码,我就陷入了麻烦之中。我抛出异常的唯一方法是创建自定义异常以及将痕迹留在内部异常中。我坚信让您的开发人员有机会解决问题,特别是如果它是由于数据造成的。我花了大部分时间来寻找破坏我的代码的数据,如果你能留下提示,你将在一年内节省数周。
答案 8 :(得分:-2)
首先,你是明确错误的。你正在拥抱一个非常严重的逻辑谬误。您希望代码通过代码假设其周围发生的所有事情都是正确的,是正确的。好像正确是某种神奇的小精灵尘埃,你只需喷到处处。
一旦暴露出来,所有的错误都是或者看起来很愚蠢。但它的这样的检查可以取笑它们以暴露自己。在那之前,虫子是看不见的。对于大型且复杂的项目,您不知道谁将找到该错误或在何种条件下找到它们。为弹性设计的代码通常在整个地方都有这样的检查,并检查每个必须包含错误值的函数的返回值。所以你最终编码“我不能这样做,因为我依赖的子功能不起作用”语义实际上是正确处理的。这样做的重要价值在于您通常可以非常轻松地实现解决方案或自我感知的调试工具。 为什么你想做这样的事情是因为最困难的错误通常依赖于这两个属性来正确调试。
向开发人员学习一些经验教训。他们把那样的支票放在那里,因为他们不知道为什么有时他们会从功能中得到奇怪的结果。你称他们天真或过于谨慎,因为你拥有的一个狭隘的知识,他们没有。但是当你调试一些讨厌的东西时,你会想知道为什么你的代码中没有这样的检查,而你最终会因为无法在第一时间发现错误而看起来很天真。
简而言之:通过假设周围环境的稳健性,不会使代码变得健壮。