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

时间:2014-03-17 11:56:01

标签: c# exception-handling

我遇到了this MSDN page声明:

  

请勿故意从您自己的源代码中抛出ExceptionSystemExceptionNullReferenceExceptionIndexOutOfRangeException

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

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

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

5 个答案:

答案 0 :(得分:85)

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

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

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

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

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

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

答案 1 :(得分:36)

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

obj.SomeMethod(); // this is actually an extension method
NullReferenceExceptionobj时,

会抛出null

答案 2 :(得分:3)

正如您所指出的那样,在主题在抛出异常时要避免的事项下的文章Creating and Throwing Exceptions (C# Programmming Guide)中,Microsoft确实将System.IndexOutOfRangeException列为不应该是的异常类型故意从您自己的源代码中抛出。

然而,相比之下,在文章throw (C# Reference)中,微软似乎违反了自己的指导方针。以下是Microsoft在其示例中包含的方法:

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

因此,微软本身并不一致,因为它证明了在IndexOutOfRangeException的文档中抛出了throw

这让我相信,至少在IndexOutOfRangeException的情况下,程序员可能会抛出异常类型 的情况。被视为可接受的做法。

答案 3 :(得分:1)

当我读到你的问题时,我问自己在什么条件下会想要抛出异常类型NullReferenceExceptionInvalidCastExceptionArgumentOutOfRangeException

在我看来,当遇到其中一种异常类型时,我(开发人员)会对编译器正在与我交谈的意义上的警告感到担忧。因此,允许您(开发人员)抛出此类异常类型等同于(编译器)销售责任。例如,这表明编译器现在应该允许开发人员决定对象是否为null。但做出这样的决定应该是编译器的工作。

PS:自2003年以来,我一直在开发自己的例外,所以我可以按照自己的意愿抛出它们。我认为这样做是最好的做法。

答案 4 :(得分:0)

将关于NullReferenceExceptionIndexOutOfBoundsException的讨论放在一边:

如何捕捉和扔System.Exception。我在代码中抛出了很多此类异常,但是我从来没有被它搞砸。同样,我经常遇到非特定的Exception类型,它对我来说也很好用。那为什么呢?

通常用户会争辩说,他们应该能够区分错误原因。根据我的经验,在极少数情况下,您希望以不同的方式处理不同的异常类型。对于这些情况,您希望用户以编程方式处理错误,则应抛出更具体的异常类型。对于其他情况,我对通用最佳实践准则并不信服。

因此,关于抛出Exception,我认为没有理由在所有情况下都禁止这样做。

编辑:同样从MSDN页面:

在普通执行过程中,不应使用异常来更改程序的流程。异常应仅用于报告和处理错误情况。

对于不同的异常类型,使用具有单独逻辑的catch子句也不是最佳做法。