为什么我们不抛出这些异常?

我看到 这个 MSDN 页面上面写着:

不要故意从自己的源代码中抛出 ExceptionSystemExceptionNullReferenceExceptionIndexOutOfRangeException 异常

不幸的是,它并没有解释原因。我可以猜到原因,但我希望在这个问题上更权威的人可以提供他们的见解。

前两个看起来有些道理,但是后两个看起来像是你想要雇佣的人(事实上,我已经雇佣了)。

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

31608 次浏览

我怀疑最后两个的意图是防止与具有预期意义的内置异常混淆。然而,我认为 如果保留异常的确切意图: 它是正确的 throw。例如,如果您正在编写一个自定义集合,那么使用 IndexOutOfRangeException似乎是完全合理的——比 ArgumentOutOfRangeException更清晰和更具体,IMO。虽然 List<T>可能会选择后者,但是在 BCL 中有 至少41个位置(不包括阵列)可以抛出定制的 IndexOutOfRangeException——没有一个是“低水平”足以获得特殊豁免。所以,我认为你可以理直气壮地说那个指导方针很愚蠢。同样,NullReferenceException在扩展方法中也很有用——如果你想保留以下语义:

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

objnull时抛出 NullReferenceException

Exception 是所有异常的基本类型,因此非常不具体。不应该引发此异常,因为它根本不包含任何有用的信息。为异常调用代码捕获不能消除故意抛出的异常(来自您的逻辑)与其他完全不需要的系统异常之间的歧义,并指出真正的错误。

同样的道理也适用于 SystemException。如果查看派生类型列表,可以看到大量其他语义非常不同的异常。

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

There are a few other exceptions like those two, for example InvalidCastException or DivideByZeroException, which are thrown for specific faults in your code and usually mean that you are doing something wrong or you are not checking for some invalid values first. By throwing them knowingly from your code, you are just making it harder for the calling code to determine whether they were thrown due some fault in the code, or just because you decided to reuse them for something in your implementation.

当然,这些规则也有一些例外。如果您正在构建可能导致与现有异常完全匹配的异常,那么可以随意使用该异常,特别是如果您试图匹配某些内置行为。那么请确保您选择了一个非常特定的异常类型。

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

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

在我看来,当遇到这些异常类型之一时,我(开发人员)会对这个警告感到担心,因为编译器在跟我说话。因此,允许您(开发人员)抛出此类异常类型等同于(编译器)出售责任。例如,这表明编译器现在应该允许开发人员决定一个对象是否是 null。但是做出这样的判断应该真的是编译器的工作。

PS: 自2003年以来,我一直在开发自己的异常,这样我就可以随心所欲地抛出它们。我认为这样做是最好的做法。

正如您所指出的,在 抛出异常时要避免的事情主题下的 创建和抛出异常(C # 编程指南)文章中,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];
}

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

这使我相信,至少在 IndexOutOfRangeException的情况下,可能会出现程序员抛出异常类型 can并被认为是可接受的实践的情况。

抛开关于 NullReferenceExceptionIndexOutOfBoundsException的讨论不谈:

接住并投出 System.Exception怎么样。我已经在我的代码中抛出了很多这种类型的异常,我从来没有被它搞砸过。类似地,我经常捕捉到非特定的 Exception类型,它对我也非常有用。为什么呢?

通常用户认为,他们应该能够区分错误的原因。根据我的经验,只有很少的几种情况需要以不同的方式处理不同的异常类型。对于那些希望用户以编程方式处理错误的情况,应该引发更具体的异常类型。对于其他情况,我不相信一般的最佳实践指南。

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

EDIT: also from the MSDN page:

异常不应该用于作为普通执行的一部分更改程序流。异常只能用于报告和处理错误条件。

Overdoing catch clauses with individual logic for different exception types are not best practice, either.