C# 我如何解释if(xyz==null)检查不是;“保护性”;

C# 我如何解释if(xyz==null)检查不是;“保护性”;,c#,preconditions,C#,Preconditions,我有几个开发人员经常进行If null检查 例如: Run(Order order) { if (order == null) return; } 在他们的代码中,因为如果有人传入null参数,他们认为自己是在保护自己的类。我试图告诉他们逻辑中的缺陷,因为如果有人在本例中传入null,很可能是消费者代码的问题,而不是这个类抛出异常并快速失败,它会优雅地处理消费者的不良行为并不断地处理 另一个建议是让前置条件或保护类快速失败并抛出异常。任何事情,但忽略这一事实,即消费者可能有一些其他问题,我

我有几个开发人员经常进行If null检查

例如:

Run(Order order)
{
  if (order == null) return;
}
在他们的代码中,因为如果有人传入null参数,他们认为自己是在保护自己的类。我试图告诉他们逻辑中的缺陷,因为如果有人在本例中传入null,很可能是消费者代码的问题,而不是这个类抛出异常并快速失败,它会优雅地处理消费者的不良行为并不断地处理

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

我怎样才能让同学们理解你们班不应该如此宽容。如果有人没有传递好的数据,应该告诉他们


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

这取决于具体情况。正如您所指出的那样,给出诸如“不要在代码中设置空检查”之类的一般性建议是不明智的。类的契约应该定义什么是合法的,什么是不合法的。但是,如果合同明确指出传入null是不可接受的,那么异常确实是一个适当的响应。

我没有看完它,而是(幻灯片+音频)关于合同设计的主题。这些家伙都有这个概念,所以如果有人能解释的话,那就是他们:-)

如果你的类不能接受
null
参数,那么最好的做法是:

if (arg == null)
    throw new ArgumentNullException();
这比将
NullPointerException
放在堆栈的更深处要好得多。在最坏的情况下,您将把
null
缓存到某个地方,直到很久以后才触发异常,然后看看调试这个问题会有多有趣


正如其他人所说,有时候合同上说
null
是可以的。在这种情况下,在代码的某些部分使用一个guard子句是正确的——尽管如此,我认为最好的设计是添加一个重载,而不使用可选的空参数。

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


正如其他人所说,与在生产过程中遇到神秘问题相比,提前失败更可取,因为函数在预期的时间内没有做任何事情。如果函数返回空参数(如示例中所示)

即使函数不返回,只是抛出一个
NullReferenceException
,当您知道一个参数为null时,仍然需要解决一个bug。如果函数抛出一个
NullReferenceException
,则您不知道是什么
null
或是谁的错误

我想补充一点,
ArgumentNullException
接受参数是有原因的

最好是写

if(myArg == null) throw new ArgumentNullException("myArg");
而不是在没有
参数名的情况下抛出
ArgumentNullException

这样,如果一个函数有一个包含五个参数的异常,您将知道是哪个参数导致了问题。如果无法附加调试器,这一点尤其重要。(例如,在生产web服务器或最终用户计算机上)

如果要编写许多函数,这可能会带来很大的开销,特别是因为字符串没有智能感知。我编写了一个代码片段来生成这些检查:

<?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>

检查空参数
tna
抛出新ArgumentNullException的代码段
渣
膨胀
周边地区
参数
检查空值的参数
价值



有时候你无法告诉人们为什么这样的做法是错误的——他们必须自己去弄清楚。但是,您可以通过提出一些单元测试来帮助他们实现这一目标,这些单元测试会导致一些严重的故障,并让他们调试错误

如果方法的约定指定其参数不应为null,那么正确的做法是使用断言使其显式,如下所示:

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

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

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

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

从开发人员那里学习一些经验教训。他们把c