如果我要立即使用它,我应该检查参数是否为 null 吗?
Should I check if argument is null if I'm going to use it immediately?
我已经习惯了检查一个方法的参数是否为空(然后继续抛出异常)以至于我几乎不再考虑它了。
如果参数是引用类型,它就在那里:
if(arg == null)
throw new ArgumentNullException(nameof(arg));
但是如果我要立即使用 arg 怎么办?我还是应该检查一下吗?我的意思是,如果我不这样做,无论如何环境都会为我抛出(NullReferenceException)。
例如:
public int DoStuff(object o)
{
return o.GetHashCode();
}
我可以轻松编写添加空检查:
public int DoStuff(object o)
{
if(o == null)
throw new ArgumentNullException(nameof(o));
return o.GetHashCode();
}
但在这两种情况下都会抛出异常(在几乎完全相同的行中,用于调试目的)。唯一的区别是类型。
问题:关于public方法单引用类型参数,如果我要立即使用参数,如果它是null
,我还应该检查吗?
关注爱因斯坦 - "make things as simple as possible, but no simpler"。如果没有一行代码,该代码是否可以做同样的事情?如果是这样,最好将其删除。
我建议检查,因为你有两个异常类型:
- 意外
NullReferenceException
- 出了点问题,你必须调试你自己的程序
ArgumentNullException
- argument 为 null,错误的是调用者(而不是 你的代码 反应 右)
投掷 ArgumentNullException
是一种 合同:如果参数正确,我会做这件事:
// An exaggerated example
public int DoStuff(SomeObject o) {
if (o == null)
throw new ArgumentNullException(nameof(o));
else if (o.Disposed)
throw new ArgumentException(nameof(o), "o must not be disposed")
else if (o.Id > 100)
throw new ArgumentOutOfRangeException("Id ...", nameof(o));
// o meets the contract, we accept it and thus we guarantee the output
return o.SomeMethod();
}
这种验证对于 public (protected) 方法是典型的,因为它们暴露于外部世界并且可以面对任何争论;但是,在 private 方法的情况下,您可以省略契约:任何调用者都在实现该方法的 class 中。
好吧,这取决于 ;)
这是我的看法,这个问题(有点)主观。
IMO,如果代码抛出NullReferenceException,说明代码本身没有内部一致性。这是懒惰的表现,应该不惜一切代价避免。
如果一个方法抛出 NullReferenceException,这是方法的错误。
另一方面,ArgumentNullException 意味着如果参数为 null,则该方法不能 "do it's stuff"。
调用者有责任保证参数不为空,或者优雅地处理异常。
底线:ArgumentNullException 是约定,NullReferenceException 是错误。
我想如果您要立即取消对变量的引用,您可以争论两种方式,但我仍然更喜欢 ArgumentNullException。
它对正在发生的事情更加明确。异常包含为 null 的变量的名称,而 NullReferenceException 则没有。
我强烈建议您检查方法顶部的 null
。
将其视为指定以下内容的 "contract":
In order for this method to execute, this argument must be non-null.
进行 null 检查是很好的文档。您可以更进一步,使用 XML 评论,例如
/// <summary>
///
/// </summary>
/// <param name="arg1"></param>
/// <exception cref="ArgumentNullException">If <paramref name="arg1"/> is NULL.</exception>
private static void Foo(object arg1)
{
}
我想说这更多地取决于您如何处理错误,而不是它们是如何抛出的。一个例子会有所帮助。想象一下这是方法而不是你拥有的方法:
public static int DoStuff(string o) {
return Int.Parse(o);
}
如果你有这个,那就真的没关系了。
public static void main(string[] args)
{
try {
Console.Write("Enter a number: ");
value = DoStuff(Console.ReadLine());
}
catch(Exception ex) {
Console.WriteLine("An exception was thrown. Contents: " + ex.ToString());
}
}
无论哪种方式,您的程序流程都是相同的。但是如果你有以下内容:
public static void main(string[] args)
{
try {
int value = 0;
do {
try {
Console.Write("Enter a non-zero number to stop: ");
value = DoStuff(Console.ReadLine());
}
catch(ArgumentException ex) {
Console.WriteLine("Problem with input, try again. Exception message was: " + ex.Message);
continue; // Or just nothing, will still loop
}
} while(value == 0);
}
catch(Exception ex) {
Console.WriteLine("An exception was thrown. Contents: " + ex.ToString());
}
}
基本上,如果您的错误处理不关心它是哪种异常类型,那么它只是额外的代码,可能对您没有帮助。但是如果您以不同的方式处理特定的事情,那么测试并抛出更具体类型的异常可能是值得的。
但我同意德米特里 post 的观点,即 public 方法比私有方法重要得多。您对该方法的签名有点像合同。最好强制执行。
我认为您应该考虑以下几种不同的情况:
- 该方法是从您自己的程序中调用的。您不希望出现空值。您在线下某处使用该对象,如果它为空,它仍会更改状态。
- 该方法是从您自己的程序中调用的。您不希望出现空值。如果为 null,则不会更改状态,所有内容将继续保持一致。
- 该方法通过库公开或API。未知的开发人员 X 随心所欲地调用该方法。
- 你只想知道在实现东西时参数不为空。为了保护您自己和您的团队成员。
第(1)点和第(2)点是关于异常安全的。基本上这表明无论您的输入如何,数据结构的状态都应该是一致的。在大多数情况下,这可以通过简单的检查来解决;在更复杂的情况下,它可能涉及 回滚 或更复杂的机器。所以:
- 如果您将
null
值添加到数据库,您不希望它弄乱您的数据库。现在,您当然可以通过简单地检查您的假设来求解 null
值。如果参数是 null
,抛出一个 ArgumentNullException
,因为这最能描述场景。
- 如果您调用的方法不期望
null
值并且它不会影响您系统的其余部分,我通常会在某处添加注释并让自然 运行 顺其自然. NullReferenceException
充分说明了错误的性质。
- 如果是 API,人们会期待某些行为(例如
ArgumentNullException
)。此外,作为开发人员,您不应该相信外面的大坏蛋,所以无论如何您都应该检查一下。
- 如果您只是想保护自己和您的团队成员,请使用
Assertion
。断言通常以这样的方式实现,它们不会在发布代码中结束(或者至少不会影响您的速度),会自动触发断点并使您的单元测试失败。它们也很容易识别,因此不会给您的代码增加太多膨胀。
总而言之,我不会随处放支票;它们往往会使您的代码难以阅读且臃肿。也就是说,如果我要进行检查以确保 异常安全 ,最好使用最能描述错误的异常。
我已经习惯了检查一个方法的参数是否为空(然后继续抛出异常)以至于我几乎不再考虑它了。 如果参数是引用类型,它就在那里:
if(arg == null)
throw new ArgumentNullException(nameof(arg));
但是如果我要立即使用 arg 怎么办?我还是应该检查一下吗?我的意思是,如果我不这样做,无论如何环境都会为我抛出(NullReferenceException)。
例如:
public int DoStuff(object o)
{
return o.GetHashCode();
}
我可以轻松编写添加空检查:
public int DoStuff(object o)
{
if(o == null)
throw new ArgumentNullException(nameof(o));
return o.GetHashCode();
}
但在这两种情况下都会抛出异常(在几乎完全相同的行中,用于调试目的)。唯一的区别是类型。
问题:关于public方法单引用类型参数,如果我要立即使用参数,如果它是null
,我还应该检查吗?
关注爱因斯坦 - "make things as simple as possible, but no simpler"。如果没有一行代码,该代码是否可以做同样的事情?如果是这样,最好将其删除。
我建议检查,因为你有两个异常类型:
- 意外
NullReferenceException
- 出了点问题,你必须调试你自己的程序 ArgumentNullException
- argument 为 null,错误的是调用者(而不是 你的代码 反应 右)
投掷 ArgumentNullException
是一种 合同:如果参数正确,我会做这件事:
// An exaggerated example
public int DoStuff(SomeObject o) {
if (o == null)
throw new ArgumentNullException(nameof(o));
else if (o.Disposed)
throw new ArgumentException(nameof(o), "o must not be disposed")
else if (o.Id > 100)
throw new ArgumentOutOfRangeException("Id ...", nameof(o));
// o meets the contract, we accept it and thus we guarantee the output
return o.SomeMethod();
}
这种验证对于 public (protected) 方法是典型的,因为它们暴露于外部世界并且可以面对任何争论;但是,在 private 方法的情况下,您可以省略契约:任何调用者都在实现该方法的 class 中。
好吧,这取决于 ;) 这是我的看法,这个问题(有点)主观。
IMO,如果代码抛出NullReferenceException,说明代码本身没有内部一致性。这是懒惰的表现,应该不惜一切代价避免。 如果一个方法抛出 NullReferenceException,这是方法的错误。
另一方面,ArgumentNullException 意味着如果参数为 null,则该方法不能 "do it's stuff"。 调用者有责任保证参数不为空,或者优雅地处理异常。
底线:ArgumentNullException 是约定,NullReferenceException 是错误。
我想如果您要立即取消对变量的引用,您可以争论两种方式,但我仍然更喜欢 ArgumentNullException。 它对正在发生的事情更加明确。异常包含为 null 的变量的名称,而 NullReferenceException 则没有。
我强烈建议您检查方法顶部的 null
。
将其视为指定以下内容的 "contract":
In order for this method to execute, this argument must be non-null.
进行 null 检查是很好的文档。您可以更进一步,使用 XML 评论,例如
/// <summary>
///
/// </summary>
/// <param name="arg1"></param>
/// <exception cref="ArgumentNullException">If <paramref name="arg1"/> is NULL.</exception>
private static void Foo(object arg1)
{
}
我想说这更多地取决于您如何处理错误,而不是它们是如何抛出的。一个例子会有所帮助。想象一下这是方法而不是你拥有的方法:
public static int DoStuff(string o) {
return Int.Parse(o);
}
如果你有这个,那就真的没关系了。
public static void main(string[] args)
{
try {
Console.Write("Enter a number: ");
value = DoStuff(Console.ReadLine());
}
catch(Exception ex) {
Console.WriteLine("An exception was thrown. Contents: " + ex.ToString());
}
}
无论哪种方式,您的程序流程都是相同的。但是如果你有以下内容:
public static void main(string[] args)
{
try {
int value = 0;
do {
try {
Console.Write("Enter a non-zero number to stop: ");
value = DoStuff(Console.ReadLine());
}
catch(ArgumentException ex) {
Console.WriteLine("Problem with input, try again. Exception message was: " + ex.Message);
continue; // Or just nothing, will still loop
}
} while(value == 0);
}
catch(Exception ex) {
Console.WriteLine("An exception was thrown. Contents: " + ex.ToString());
}
}
基本上,如果您的错误处理不关心它是哪种异常类型,那么它只是额外的代码,可能对您没有帮助。但是如果您以不同的方式处理特定的事情,那么测试并抛出更具体类型的异常可能是值得的。
但我同意德米特里 post 的观点,即 public 方法比私有方法重要得多。您对该方法的签名有点像合同。最好强制执行。
我认为您应该考虑以下几种不同的情况:
- 该方法是从您自己的程序中调用的。您不希望出现空值。您在线下某处使用该对象,如果它为空,它仍会更改状态。
- 该方法是从您自己的程序中调用的。您不希望出现空值。如果为 null,则不会更改状态,所有内容将继续保持一致。
- 该方法通过库公开或API。未知的开发人员 X 随心所欲地调用该方法。
- 你只想知道在实现东西时参数不为空。为了保护您自己和您的团队成员。
第(1)点和第(2)点是关于异常安全的。基本上这表明无论您的输入如何,数据结构的状态都应该是一致的。在大多数情况下,这可以通过简单的检查来解决;在更复杂的情况下,它可能涉及 回滚 或更复杂的机器。所以:
- 如果您将
null
值添加到数据库,您不希望它弄乱您的数据库。现在,您当然可以通过简单地检查您的假设来求解null
值。如果参数是null
,抛出一个ArgumentNullException
,因为这最能描述场景。 - 如果您调用的方法不期望
null
值并且它不会影响您系统的其余部分,我通常会在某处添加注释并让自然 运行 顺其自然.NullReferenceException
充分说明了错误的性质。 - 如果是 API,人们会期待某些行为(例如
ArgumentNullException
)。此外,作为开发人员,您不应该相信外面的大坏蛋,所以无论如何您都应该检查一下。 - 如果您只是想保护自己和您的团队成员,请使用
Assertion
。断言通常以这样的方式实现,它们不会在发布代码中结束(或者至少不会影响您的速度),会自动触发断点并使您的单元测试失败。它们也很容易识别,因此不会给您的代码增加太多膨胀。
总而言之,我不会随处放支票;它们往往会使您的代码难以阅读且臃肿。也就是说,如果我要进行检查以确保 异常安全 ,最好使用最能描述错误的异常。