对于.NET中的无效或意外参数,应该引发哪些类型的异常?什么时候我会选择一个而不是另一个?
如果您有一个函数需要一个对应于月份的整数,并且您传入了“ 42 ”,您会使用哪个异常?即使它不是一个集合,它也会属于“超出范围”的类别吗?
参数异常.
根据实际值和最适合的异常:
ArgumentException(值有问题)
ArgumentException
ArgumentNullException(参数为空,而这是不允许的)
ArgumentNullException
ArgumentOutOfRangeException(变量值超出有效范围)
ArgumentOutOfRangeException
如果这还不够精确,只需从ArgumentException派生您自己的异常类。
尤德的回答启发了我。如果输入在任何时间都无效,则将其无效,而如果输入对于系统的当前状态无效,则将其意外的。因此,在后一种情况下,InvalidOperationException是合理的选择。
InvalidOperationException
我喜欢使用:ArgumentException,ArgumentNullException,ArgumentOutOfRangeException。
也有其他选项,它们不太关注论点本身,而是将电话作为一个整体来判断:
NotSupportedException
诀窍是抛出最能表达该方法不能以这种方式调用的原因的异常。理想情况下,异常应该详细说明什么地方出错了,为什么出错了,以及如何修复它。
我喜欢错误消息指向帮助、文档或其他资源。例如,Microsoft在其知识库文章中迈出了良好的第一步,例如当我在Internet Explorer中访问网页时,为什么会收到“ Operation Aborted ”错误信息?。当您遇到错误时,它们会将您指向错误消息中的知识库文章。他们做得不好的是他们没有告诉你,为什么它失败了。
再次感谢STW(前Yoooder)的评论。
为了回应你的跟进,我将抛出一个ArgumentOutOfRangeException。看看MSDN是怎么说这个异常的:
引发ArgumentOutOfRangeException 当调用方法时,至少 传递给的参数之一 方法不为空 引用(_ABC在Visual Basic中的_0) 并且不包含有效值。
ArgumentOutOfRangeException 当调用方法时,至少 传递给的参数之一 方法不为空 引用(_ABC在Visual Basic中的_0) 并且不包含有效值。
因此,在本例中,您正在传递一个值,但这不是一个有效值,因为您的范围是1–12。然而,你记录它的方式清楚地表明了你的API抛出了什么。因为尽管我可能会说ArgumentOutOfRangeException,但另一个开发人员可能会说ArgumentException。让它变得简单并记录行为。
参数异常:
时引发ArgumentException. 方法,并且至少有一个 传递的参数不符合 被叫方的参数说明 方法。的所有实例 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消费者)的不足。
最长的答案: 为无效参数抛出异常是不礼貌的,除非你写了一个库。 我更喜欢使用断言,原因有两个(或更多):
下面是NULL异常的处理方式(显然是讽刺):
try { library.Method(null); } catch (ArgumentNullException e) { // retry with real argument this time library.Method(realArgument); }
例外。 参数*异常是一个错误的指示,应该(我的观点)用测试来处理,并用debug.assert
顺便说一句:在这种特殊情况下,您可以使用Month类型,而不是int. C#在类型安全(aspect#rulez!)方面存在不足,但有时您可以同时防止(或在编译时捕获)这些错误。
是的,微软在这一点上是错误的。