对于.NET中的无效或意外参数,应该引发哪些异常?

对于.NET中的无效或意外参数,应该引发哪些类型的异常?什么时候我会选择一个而不是另一个?

后续行动:

如果您有一个函数需要一个对应于月份的整数,并且您传入了“ 42 ”,您会使用哪个异常?即使它不是一个集合,它也会属于“超出范围”的类别吗?

146847 次浏览

参数异常.

  • System.ArgumentException
  • System.ArgumentNullException
  • System.ArgumentOutOfRangeException

根据实际值和最适合的异常:

如果这还不够精确,只需从ArgumentException派生您自己的异常类。

尤德的回答启发了我。如果输入在任何时间都无效,则将其无效,而如果输入对于系统的当前状态无效,则将其意外的。因此,在后一种情况下,InvalidOperationException是合理的选择。

我喜欢使用:ArgumentExceptionArgumentNullExceptionArgumentOutOfRangeException

也有其他选项,它们不太关注论点本身,而是将电话作为一个整体来判断:

  • InvalidOperationException–参数可能正确,但不在对象的当前状态中。要归功于STW(之前是Yoooder)。把他的答案也投上一票。
  • NotSupportedException-传入的参数有效,但在此实现中不受支持。假设有一个FTP客户端,您在其中传递了一个客户端不支持的命令。

诀窍是抛出最能表达该方法不能以这种方式调用的原因的异常。理想情况下,异常应该详细说明什么地方出错了,为什么出错了,以及如何修复它。

我喜欢错误消息指向帮助、文档或其他资源。例如,Microsoft在其知识库文章中迈出了良好的第一步,例如当我在Internet Explorer中访问网页时,为什么会收到“ Operation Aborted ”错误信息?。当您遇到错误时,它们会将您指向错误消息中的知识库文章。他们做得不好的是他们没有告诉你,为什么它失败了。

再次感谢STW(前Yoooder)的评论。


为了回应你的跟进,我将抛出一个ArgumentOutOfRangeException。看看MSDN是怎么说这个异常的:

引发

ArgumentOutOfRangeException 当调用方法时,至少 传递给的参数之一 方法不为空 引用(_ABC在Visual Basic中的_0) 并且不包含有效值。

因此,在本例中,您正在传递一个值,但这不是一个有效值,因为您的范围是1–12。然而,你记录它的方式清楚地表明了你的API抛出了什么。因为尽管我可能会说ArgumentOutOfRangeException,但另一个开发人员可能会说ArgumentException。让它变得简单并记录行为。

参数异常

引发ArgumentException. 方法,并且至少有一个 传递的参数不符合 被叫方的参数说明 方法。的所有实例 ArgumentException应携带 有意义的错误消息描述 无效的参数以及 的值的预期范围。 参数.

对于特定类型的无效,也存在一些子类。该链接包含子类型的摘要以及应应用这些子类型的时间。

您可以使用一个标准的ArgumentException,也可以创建自己的子类。有几个特定的ArgumentException类:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

哪一个效果最好。

_0,我对_ABC投了票,但我想在列表中再添加一项:

如果参数有效,但对象处于不应使用参数的状态,则应引发System.InvalidOperationException.

取自MSDN的更新

InvalidOperationException用于 当未能调用 方法是由以下原因以外的原因引起的 参数无效.

假设你的对象有一个performAction(enmSomeAction action)方法,有效的enmSomeAction是open和close.如果连续两次调用PerformAction(EnmSomeAction.Open),则第二次调用应抛出InvalidOperationException(因为该参数有效,但不适用于控件的当前状态)。

既然你已经通过防御性编程做了正确的事情,我还有另一个例外要提一下,那就是ObjectDisposedException.如果,你的对象实现了IDisposable,那么你应该总是有一个跟踪Disposed状态的类变量。如果您的对象已被释放,并且对其调用了方法,则应引发ObjectDisposedException:

public void SomeMethod()
{
If (m_Disposed) {
throw new ObjectDisposedException("Object has been disposed")
}
// ... Normal execution code
}

更新:来回答您的后续问题:这是一种有点模糊的情况,并且由于使用泛型(不是.NET泛型意义上的)数据类型来表示一组特定的数据而变得更加复杂。枚举或其他强类型对象将是更理想的选择,但我们并不总是拥有这种控制。

我个人倾向于使用ArgumentOutOfRangeException,并提供一条消息,指出有效值为1-12。我的理由是,当您谈论月份时,假设月份的所有整数表示都是有效的,那么您期望的值在1-12范围内。如果只有某些月份(比如有31天的月份)是有效的,那么您就不会处理范围本身,我会抛出一个通用的ArgumentException来指示有效值,并且我还会在方法的注释中记录它们。

简短回答:
都不是

更长的答案:
使用参数*Exception(除非在其上是产品的库中,如组件库)是一种味道。异常是为了处理异常情况,而不是错误,也不是用户(即API消费者)的不足。

最长的答案:
为无效参数抛出异常是不礼貌的,除非你写了一个库。
我更喜欢使用断言,原因有两个(或更多):

  • 断言不需要测试, 而抛出断言执行,并测试 针对ArgumentNullException查找 荒谬(试试看)。
  • 断言更好地传达了单元的预期用途, 更接近可执行 文档而不是类行为 规格。
  • 您可以更改断言违规的行为。例如,在调试编译中,一个消息框是可以的,这样你的QA就会马上用它来攻击你(你也会让你的IDE在它发生的那一行中断),而在单元测试中,你可以将断言失败指示为测试失败。

下面是NULL异常的处理方式(显然是讽刺):

try {
library.Method(null);
}
catch (ArgumentNullException e) {
// retry with real argument this time
library.Method(realArgument);
}
当情况是预期的但例外时(发生消费者无法控制的事情,如IO故障),应使用

例外。 参数*异常是一个错误的指示,应该(我的观点)用测试来处理,并用debug.assert

来辅助

顺便说一句:在这种特殊情况下,您可以使用Month类型,而不是int. C#在类型安全(aspect#rulez!)方面存在不足,但有时您可以同时防止(或在编译时捕获)这些错误。

是的,微软在这一点上是错误的。