在C#中,是否有任何好的理由(除了更好的错误消息)将参数空值检查添加到null不是有效值的每个函数?显然,使用s的代码无论如何都会抛出异常。这样的检查使代码变得更慢,更难维护。
void f(SomeType s)
{
if (s == null)
{
throw new ArgumentNullException("s cannot be null.");
}
// Use s
}
答案 0 :(得分:142)
是的,有充分的理由:
NullReferenceException
至于你的反对意见:
对于你的断言:
显然,使用s的代码无论如何都会抛出异常。
真的?考虑:
void f(SomeType s)
{
// Use s
Console.WriteLine("I've got a message of {0}", s);
}
使用s
,但它不会抛出异常。如果s
无效为空,并且表明出现了问题,则异常是最合适的行为。
现在其中你放置那些参数验证检查是另一回事。您可能决定信任自己类中的所有代码,因此不必担心私有方法。您可能决定信任组件的其余部分,因此不必担心内部方法。您几乎肯定会验证公共方法的参数。
附注:ArgumentNullException
的单参数构造函数重载应该只是参数名称,因此您的测试应该是:
if (s == null)
{
throw new ArgumentNullException("s");
}
或者你可以创建一个扩展方法,允许有点terser:
s.ThrowIfNull("s");
在我的(通用)扩展方法的版本中,如果它是非null,我会返回原始值,允许你编写如下内容:
this.name = name.ThrowIfNull("name");
如果您对此不太感兴趣,也可以使用不带参数名称的重载。
答案 1 :(得分:45)
我同意Jon,但我会补充一点。
我对何时添加显式空检查的态度是基于这些前提:
throw
语句是语句。 if
的后果是声明。throw
if (x == null) throw whatever;
如果没有可能的方式要执行该语句,则无法对其进行测试,应将其替换为Debug.Assert(x != null);
。
如果有可能的方法来执行该语句,那么编写语句,然后编写一个单元测试来运行它。
公共类型的公共方法以这种方式检查它们的参数是非常重要的 。你不知道你的用户会做什么疯狂的事情。给他们“嘿,你笨蛋,你做错了!”尽快例外。
相比之下,私有类型的私有方法更有可能处于控制参数的情况下,并且可以强有力地保证参数永远不为空;使用断言记录该不变量。答案 2 :(得分:7)
如果您没有明确的if
检查,如果您不拥有代码,则很难找出 是null
。
如果您从没有源代码的库中深入了解NullReferenceException
,那么您可能很难弄清楚您做错了什么。
这些if
检查不会使您的代码明显变慢。
请注意,ArgumentNullException
constructor的参数是参数名称,而不是消息
你的代码应该是
if (s == null) throw new ArgumentNullException("s");
我编写了一段代码片段,以便更轻松:
<?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 :(得分:4)
如果您需要更好的方法来确保不将任何null对象作为参数,您可能需要查看Code Contracts。
答案 4 :(得分:3)
我已经使用了一年了:
select joining_column, count(joining_col) from <tablename>
group by joining_col
这是一个oneliner,丢弃(_ = s ?? throw new ArgumentNullException(nameof(s));
)意味着没有不必要的分配。
答案 5 :(得分:2)
主要的好处是,您从一开始就明确了解方法的要求。这使得处理代码的其他开发人员明白,调用者向您的方法发送空值确实是一个错误。
在执行任何其他代码之前,检查还将暂停执行该方法。这意味着您不必担心未完成的方法所做的修改。
答案 6 :(得分:2)
当你遇到异常时,它会保存一些调试。
ArgumentNullException明确声明它是“s”,它是null。
如果你没有那个检查并且让代码爆破op,你会从该方法中的某个未识别的行获得NullReferenceException。在发布版本中,您不会获得行号!
答案 7 :(得分:0)
commit()
所以对你的例子来说:
int i = Age ?? 0;
或者:
if (age == null || age == 0)
或者:
if (age.GetValueOrDefault(0) == 0)
或三元:
if ((age ?? 0) == 0)
答案 8 :(得分:-1)
原始代码:
void f(SomeType s)
{
if (s == null)
{
throw new ArgumentNullException("s cannot be null.");
}
// Use s
}
将其重写为:
void f(SomeType s)
{
if (s == null) throw new ArgumentNullException(nameof(s));
}
[编辑]使用nameof
重写的原因是因为它允许更容易的重构。如果变量s
的名称发生了变化,那么调试消息也会更新,而如果您只是硬编码变量的名称,那么随着时间的推移,它最终会过时。这是工业中使用的一种很好的做法。