为什么我们不抛出这些例外?

Don*_*ott 103 c# exception-handling

我遇到了这个MSDN页面,其中说明:

不要故意从您自己的源代码中抛出Exception,SystemException,NullReferenceExceptionIndexOutOfRangeException.

不幸的是,它没有解释原因.我可以猜出原因,但我希望有更多权威人士可以提供他们的见解.

前两个有一些明显的意义,但后两个看起来像你想要使用的(事实上,我有).

此外,这些是唯一应该避免的例外吗?如果有其他人,他们是什么,为什么他们也应该避免?

pok*_*oke 92

Exception是所有异常的基本类型,因此非常不明确.您不应该抛出此异常,因为它根本不包含任何有用的信息.调用代码捕获异常无法消除故意抛出的异常(来自您的逻辑)与其他完全不受欢迎的系统异常并指出真正的错误.

同样的原因也适用于SystemException.如果查看派生类型列表,可以看到大量具有非常不同语义的异常.

NullReferenceException而且IndexOutOfRangeException是另一种.现在这些是非常具体的例外,所以抛出它们可能没问题.但是,您仍然不想抛出这些,因为它们通常意味着您的逻辑中存在一些实际错误.例如,null引用异常意味着您正在尝试访问对象的成员null.如果你的代码中有可能,那么你应该总是明确地检查null并抛出一个更有用的异常(例如ArgumentNullException).类似地,IndexOutOfRangeException当您访问无效索引时(在数组而不是列表上),会出现s.您应该始终确保首先不要这样做并首先检查例如数组的边界.

还有一些其他例外,比如这两个例子,InvalidCastException或者DivideByZeroException,它们会因代码中的特定错误而抛出,通常意味着您做错了或者您没有先检查一些无效值.通过故意从代码中抛出它们,您只是让调用代码更难以确定它们是否因代码中的某些错误而被抛出,或者仅仅因为您决定在实现中重用它们.

当然,这些规则有一些例外(哈).如果您正在构建可能导致与现有异常完全匹配的异常的内容,则可以随意使用它,尤其是在您尝试匹配某些内置行为时.只需确保选择一种非常具体的异常类型.

通常,除非您找到满足您需求的(特定)异常,否则应始终考虑为特定的预期异常创建自己的异常类型.特别是在编写库代码时,这对于分离异常源非常有用.

  • @delnan如果你正在实现`IList`,那么你将抛出一个`ArgumentOutOfRangeException`作为[interface documentation](http://msdn.microsoft.com/en-us/library/system.collections.ilist.item. aspx)建议.`IndexOutOfRangeException`用于数组,据我所知,你不能重新实现数组. (5认同)
  • 第三部分对我来说没什么意义.当然,你应该宁愿避免引起这些错误,但是当你编写`IList`实现时,它不能影响所请求的索引,它是*当索引无效时调用者的*逻辑错误,而你只能通过抛出适当的异常来通知他们这个逻辑错误.为什么`IndexOutOfRangeException`不合适? (2认同)
  • 什么也可能有点相关,`NullReferenceException`通常在内部抛出作为`AccessViolationException`的特殊情况(IIRC测试类似于`cmp [addr],addr`,即它试图取消引用指针,如果它因访问冲突而失败,它会在生成的中断处理程序中处理NRE和AVE之间的差异.因此除了语义原因外,还有一些作弊行为.它也可能有助于阻止你在没有帮助的情况下手动检查`null` - 如果你打算抛出一个NRE,为什么不让.NET做呢? (2认同)

Mar*_*ell 36

我怀疑最后2的意图是防止与具有预期意义的内置异常混淆.但是,我认为如果你保留了异常的确切意图:它是正确的throw.例如,如果您正在编写自定义集合,那么使用它似乎是完全合理的IndexOutOfRangeException- 比IMO更清晰,更具体ArgumentOutOfRangeException.虽然List<T>可能选择后者,但BCL(不包括阵列)中至少有 41个地方(反射器礼貌)抛出定制IndexOutOfRangeException- 其中没有一个是"低水平"足以值得特别豁免.所以,是的,我认为你可以公正地说这个指导方针是愚蠢的.同样,NullReferenceException在扩展方法中有用 - 如果要保留以下语义:

obj.SomeMethod(); // this is actually an extension method
Run Code Online (Sandbox Code Playgroud)

抛出一个NullReferenceExceptionobjnull.

  • 我认为扩展方法仍然应该抛出一个`ArgumentNullException`而不是一个`NullReferenceException`.即使扩展方法的语法糖允许与普通成员访问相同的语法,它仍然有很大不同.从"MyStaticHelpers.SomeMethod(obj)"中获取NRE是错误的. (16认同)
  • @PugFugly需要2秒钟来查看扩展方法示例:不,如果没有必须测试它,就不会抛出它.如果`SomeMethod()`不需要执行成员访问,则强制它不正确.同样:注意到这一点了BCL中的41个地方是创建自定义`IndexOutOfRangeException`,并创建自定义的16个地方`NullReferenceException` (5认同)
  • "如果你保留了异常的确切意图" - 如果是这种情况,那么在没有你必须首先测试它的情况下会抛出异常?如果你已经对它进行了测试,那么这不是一个例外吗? (2认同)

Dav*_*dRR 5

如您所指出,在主题“ 抛出异常时应避免的事情下的“ 创建和抛出异常”(C#编程指南)中,Microsoft确实确实将异常类型列为不应从您自己的源代码中故意抛出的异常类型。System.IndexOutOfRangeException

相反,在本文(《 C#参考》)中,Microsoft似乎违反了自己的准则。这是Microsoft包含在其示例中的方法:

static int GetNumber(int index)
{
    int[] nums = { 300, 600, 900 };
    if (index > nums.Length)
    {
        throw new IndexOutOfRangeException();
    }
    return nums[index];
}
Run Code Online (Sandbox Code Playgroud)

因此,Microsoft本身并不一致,因为它IndexOutOfRangeException在其文档中证明了throw

这使我相信,至少对于的情况IndexOutOfRangeException,程序员有时可能会抛出该异常类型,并被认为是可以接受的做法。