抛出特定异常子类的目的是什么?

时间:2010-10-20 17:07:11

标签: .net exception-handling

为什么最好抛出此异常

Throw New DivideByZeroException("You can't divide by zero")

这个一般的:

Throw New Exception("You can't divide by zero")

在这个特定的例子中获得了什么好处?消息已经告诉了所有。从基础Exception类继承的标准子类是否有不同的基础方法?我没有看到一个案例,但我必须承认我倾向于抛出基础异常。

8 个答案:

答案 0 :(得分:20)

异常的类型允许异常处理程序对其进行过滤。如果您抛出的只是类型Exception的异常,处理程序将如何知道要捕获的异常以及允许在调用堆栈中传播的异常?

例如,如果你总是抛出Exception

void Foo(string item) {
  try {
    if (Bar(item)) { 
      Console.WriteLine("BAR!");
    }
  } catch (Exception e) {
    Console.WriteLine("Something bad?");
  }
}

bool Bar(string item) {
  if (item == null) {
    throw new Exception("Argument is null!");
  }

  return Int32.Parse(item) != 0;
}

调用者Foo如何知道是否发生了空异常或Int32.Parse()失败了?它必须检查抛出的异常的类型(或做一些讨厌的字符串比较)。

如果你得到ThreadAbortExceptionOutOfMemoryException可能会出现在你不希望异常的地方,那就更令人担忧了。在这些情况下,如果您的捕获代码仅捕获Exception,则可能会掩盖这些(重要)异常并对您的程序(或系统)状态造成损害。

示例代码应为:

void Foo(string item) {
  try {
    if (Bar(item)) { 
      Console.WriteLine("BAR!");
    }
  } catch (ArgumentNullException ae) {
    Console.WriteLine("Null strings cannot be passed!");
  } catch (FormatException fe) {
    Console.WriteLine("Please enter a valid integer!");
  }
}

bool Bar(string item) {
  if (item == null) {
    throw new ArgumentNullException("item");
  }

  return Int32.Parse(item) != 0;
}

答案 1 :(得分:7)

因为你可以有多个catch语句并以不同的方式处理不同的错误。

例如,DivideByZero异常可能会提示用户更正条目,而FileNotFound异常可能会提醒用户程序无法继续并关闭程序。

这里有一篇很好的深度文章回答了这个问题:http://blogs.msdn.com/b/dotnet/archive/2009/02/19/why-catch-exception-empty-catch-is-bad.aspx

答案 2 :(得分:5)

您可以捕获多种类型的异常,而不是根据错误流中发送的文本进行过滤。每个人都可能有一种非常具体的方式来执行恢复。文本只是为用户或调试器提供一些反馈,但程序关心异常类型。出于同样的原因,用户创建的类存在多态性,但存在例外情况。

为不同的异常类型包含多个catch语句比解析消息文本以理解正确处理问题需要做什么要容易得多。

答案 3 :(得分:4)

直接来自MSDN - Exception Handling

  

在理解为什么会在给定的上下文中抛出特定异常时,请考虑捕获。

     

您应该只捕获可以从中恢复的异常。例如,尝试打开不存在的文件导致的FileNotFoundException可以由应用程序处理,因为它可以将问题传达给用户并允许用户指定不同的文件名或创建文件。不应处理打开生成ExecutionEngineException的文件的请求,因为无法确切知道异常的根本原因,并且应用程序无法确保继续执行是安全的。

不要过度使用catch,因为从catch块中抛出另一个异常将重置堆栈跟踪并导致丢失重要的调试信息,MSDN建议再次:

  

不要过度使用捕获。通常应允许例外传播调用堆栈。

     

捕获无法合法处理的异常会隐藏重要的调试信息。

最后,捕获异常应该是为了处理您希望在某些常见情况下发生的特定异常,您希望在异常捕获时记录或具有某些特定行为,否则只需throw它,Eric Lippert他自己在博客上推荐(见Too much reuse文章)。

try {
    ...
} catch (Exception ex) {
    throw; // This does not reset the stack trace.
}

而不是:

try {
    ...
} catch (Exception ex) {
    throw ex; // This does reset the stack trace.
}

最后,Exception并非强制性要求提供某些特殊性作为补充属性或方法,或者无论如何,它的名称都是为自己说明,允许您根据特定类型过滤捕获异常。

编辑#1

关于Eric Lippert博客上错误处理的另一个有趣链接: Vexing exceptions

答案 4 :(得分:3)

Exception的各个子类具有语义含义 - ArgumentNullException指示与生成DivideByZeroException的问题不同的问题,程序员可以以不同方式处理这些问题。此外,如果程序员选择使用子类,则子类可以定义可以帮助诊断或处理问题的额外属性或方法。

答案 5 :(得分:3)

异常通常是(1)被捕获,记录和重新抛出,(2)被捕获和处理,或(3)未被捕获。

如果它被捕获,记录并重新抛出,那么可能会有一些重要的额外信息隐藏在特定的异常类型中,该类型允许日志记录代码转储更多丰富的信息,以便分析师试图调试问题导致异常可以更有效地做到这一点。

如果它被捕获并处理,那么您需要知道如何处理该问题。如果异常属于特定类型,那么对于正在编写处理程序的开发人员来说,这是一个很大的线索,他们是否可以处理异常。您应该只处理您期望的异常并知道如何从中恢复。

如果没有被捕获,那么该过程将会停止,并且可能会在某处发送崩溃转储。现在我们回到案例(1)。

在所有三种情况下,具有更多类型信息的情况优于具有更少的类型信息。

答案 6 :(得分:1)

try {
    Do();
}
catch (MyException)
{
    // reaction on MyException
}
catch (AnotherException)
{
    // another reaction on AnotherException
{
// SomeException will not be caught


void Do()
{
    if (...)
        throw new MyException();
    else if (...)
        throw new AnotherException();
    else
        throw new SomeException();
}

答案 7 :(得分:1)

在设计良好的异常层次结构中,具有不同类型的异常使得可以使catch语句在不同情况下采取不同的操作。理想情况下,如果特定操作无法完成,则会从函数中出现一系列异常,但在尝试操作之前应该适用的类不变量可能仍然存在,唯一的例外是操作失败所暗示的那些。例如,如果集合看起来有效但请求的对象不存在,则集合的get-object方法应该从该族中抛出异常。

如果一个函数以这样一种方式失败,表明类不变量在被调用时没有保持,或者在它返回后不再保持,它应该从另一个族中抛出异常。请注意,如果异常发生的唯一方式是违反不变量,则函数可能适合从第一个族中捕获异常并重新抛出第二个族。如果异常来自在函数返回后永远不会被使用的对象,那么有时候捕获第二种类型的异常并抛出第一种类型的异常也是合适的。

如果真的很糟糕(例如OutOfMemoryException,CpuCatchingFireException等),那么它应该是与前两个层次分开的另一个层次结构。

现有的异常不遵循该模式,但可以将其用于创建的任何新异常。