4

我有一些开发人员经常进行 If null 检查

例如:

Run(Order order)
{
  if (order == null) return;
}

在他们的代码中,因为如果有人传入一个为空的参数,他们认为他们正在保护他们的类。我试图告诉他们逻辑中的缺陷,因为如果在这种情况下有人传入 null,这很可能是消费者代码的问题,而不是这个类抛出异常并快速失败,它优雅地处理了不良行为消费者并继续走开。

另一个建议是设置快速失败并抛出异常的前置条件或保护类。任何事情,但忽略消费者可能有其他问题的事实,我帮助掩盖它。

我如何让人们欣赏你的班级不应该如此宽容的事实。如果有人没有传递好的数据,他们应该被告知。

有什么好的文章或建议可以帮助我理解这一点吗?

4

9 回答 9

13

如果您的班级不能接受null参数,那么最好的办法是:

if (arg == null)
    throw new ArgumentNullException();

这比NullPointerException深入堆栈要好得多。在最坏的情况下,您会将其缓存在某个地方,直到很久以后才会真正触发异常,然后null看看您调试问题会有多有趣。

正如其他人所说,有时合同说null没关系。在这种情况下,在代码的某些部分周围有一个保护子句是正确的——尽管即使那样我会说最好的设计是添加一个没有可选空参数的重载。

于 2009-10-30T02:30:57.360 回答
7

这真的取决于具体情况。正如您所暗示的那样,很少建议提供诸如“不要在代码中进行空检查”之类的一般性建议。类的契约应该定义什么是合法的,什么不是。但是如果合约明确规定传入 null 是不可接受的,那么异常确实是一个合适的响应。

于 2009-10-30T02:26:49.997 回答
3

正如其他人所说的那样,尽早失败比在生产中遇到神秘问题要好得多,因为该功能没有按预期执行任何操作。如果函数返回空参数,如您的示例中所示)。

即使函数没有返回而只是抛出 a NullReferenceException,当您知道参数为空时,也更容易解决错误。如果一个函数抛出 a NullReferenceException,你不知道null它是什么或是谁的错。

我想添加ArgumentNullException一个参数是有原因的。

最好写

if(myArg == null) throw new ArgumentNullException("myArg");

而不是抛出一个ArgumentNullException没有 a 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>
于 2009-10-30T02:50:17.127 回答
2

我没看过,但是eiffel.com 有 2 个关于按合同设计的主题的演示文稿(幻灯片+音频)。这些人发明了这个概念,所以如果有人能解释它就是他们:-)

于 2009-10-30T02:28:53.353 回答
2

.net 4.0 中的代码契约有望使这种行为更加一致。任何讨论代码契约的文章都将有助于理解这个想法,并且在未来,这种语法将提供方法。

http://blogs.msdn.com/bclteam/archive/2008/11/11/introduction-to-code-contracts-melitta-andersen.aspx

于 2009-10-30T02:36:27.607 回答
1

有时你不能告诉人们为什么这样的做法是错误的——他们必须自己弄清楚。但是您可以通过提出一些单元测试来帮助他们到达那里,这些单元测试会因这个问题而导致一些令人讨厌的失败,并让他们调试错误。

于 2009-10-30T02:53:22.190 回答
1

如果方法的协定规定其参数不应为空,那么正确的做法是使用 Assert 使其显式化,如下所示:

Debug.Assert( item != null, "Null items are not supported );

当使用调试配置构建可执行文件时,这将很快失败,但在使用发布配置构建时将呈现零性能下降。

于 2009-10-30T02:58:40.493 回答
1

这似乎是一个关于如何最好地编写可管理的代码的问题。我的新信念是,您必须假设您对代码的所有使用者一无所知。我假设我或具有深厚知识的人会使用我的代码,这让我自己陷入了麻烦。我要添加到抛出异常的唯一事情是创建自定义异常以及将面包屑留在内部异常中。我坚信让您的开发人员有机会解决问题,特别是如果它是由于数据引起的。我大部分时间都在寻找破坏我的代码的数据,如果你能留下提示,你将在一年中节省几周的时间。

于 2009-10-30T03:55:05.507 回答
-2

那么首先,你是明确不正确的。你正在接受一个非常严重的逻辑谬误。你希望你的代码是正确的,因为代码假设周围发生的一切都是正确的。就好像正确性是某种神奇的精灵尘埃,你只需要把它喷洒到任何地方。

一旦暴露出来,所有的错误都是愚蠢的或看起来很愚蠢。但是像这样的检查会挑逗他们以暴露自己。在那之前,错误是不可见的。而对于足够大且足够复杂的项目,你不知道谁会发现这个 bug,或者在什么条件下会发现它们。为弹性而设计的代码通常在所有地方都有这样的检查,并且还检查每个必须包含错误值的函数的返回值。因此,您最终编码了一个“我不能这样做,因为我依赖的子功能不起作用”的语义实际上得到了正确处理。这样做的巨大价值在于,您通常可以很容易地实现变通或自我感知的调试工具。 为什么你想做这样的事情是因为最困难的错误通常依赖于这两个属性来正确调试。

向您的开发人员学习一些经验教训。他们在那里放了这样的检查,因为他们不知道为什么有时会从函数中得到奇怪的结果。你称他们为幼稚或过于谨慎,因为你拥有他们没有的一项狭隘的知识。但是当你在调试一些讨厌的东西时,你会想知道为什么你的代码中没有这样的检查,你最终会因为无法首先发现错误而看起来很天真。

简而言之:通过假设周围环境的健壮性,不会使代码变得健壮。

于 2009-10-30T03:01:27.453 回答