C# 我如何解释if(xyz==null)检查不是;“保护性”;
我有几个开发人员经常进行If null检查 例如:C# 我如何解释if(xyz==null)检查不是;“保护性”;,c#,preconditions,C#,Preconditions,我有几个开发人员经常进行If null检查 例如: Run(Order order) { if (order == null) return; } 在他们的代码中,因为如果有人传入null参数,他们认为自己是在保护自己的类。我试图告诉他们逻辑中的缺陷,因为如果有人在本例中传入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