3

处理异常而不必到处放置 try/catch 块的最佳实践是什么?

我有创建一个专门用于接收和处理异常的类的想法,但我想知道它是否是一个好的设计理念。这样的类会收到一个异常,然后根据其类型或错误代码决定如何处理它,甚至可以解析堆栈跟踪以获取特定信息等。

这是背后的基本思想和实现:

public class ExceptionHandler
{
    public static void Handle(Exception e)
    {
        if (e.GetBaseException().GetType() == typeof(ArgumentException))
        {
            Console.WriteLine("You caught an ArgumentException.");
        }
        else
        {
            Console.WriteLine("You did not catch an exception."); 
            throw e;   // re-throwing is the default behavior
        }
    }
}

public static class ExceptionThrower
{
    public static void TriggerException(bool isTrigger)
    {
        if (isTrigger)
            throw new ArgumentException("You threw an exception.");
        else
            Console.WriteLine("You did not throw an exception."); 
    }
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            ExceptionThrower.TriggerException(true); 
        }
        catch(Exception e)
        {
            ExceptionHandler.Handle(e);  
        }
        Console.ReadLine(); 
    }
}

我认为这将是一项有趣的尝试,因为理论上您在 main() 方法调用周围只需要一个或很少的 try/catch 块,并让异常类处理其他所有事情,包括重新抛出、处理、日志记录等。

想法?

4

4 回答 4

6

好的,这可能不是您想要的答案,但是...

我通常对通用异常处理类的想法过敏。你几乎可以听到它本身是如何自相矛盾的。异常是异常事件。异常事件不能以一般方式处理,但无论它们出现在哪里都需要定制处理,这实质上意味着您的代码应该做两件事:

  1. 对任何输入保持防御,以避免首先出现异常
  2. try..catch块放在任何对捕获和处理异常有意义的地方(注意,这意味着您不应该try..catch在所有方法中都有块)

那么,捕获和处理异常在哪里有意义呢?简而言之,您的代码拥有能够处理异常的知识。如果没有,让异常向上冒泡到调用者。我认为您应该捕获所有异常并针对要做什么具有一些通用默认行为的唯一地方,它位于应用程序的顶层。这通常是 UI。

于 2011-04-06T22:49:51.497 回答
5

实际上,您在生产代码中看不到类似的设计有充分理由的。

首先,这样的设计并不能帮助你减少代码中try/catch对的数量(这应该是显而易见的)。它可以帮助您减少catch给定语句的数量try,因为您可以捕获System.Exception并转发到ExceptionHandler...

但接下来呢?

每个异常都需要以不同的方式处理。怎么会ExceptionHandler知道到底该做什么?您可以尝试通过多种方式解决此问题,例如:

  1. 在虚拟方法中派生ExceptionHandler并放置处理异常的代码
  2. 将多个Action<Exception>实例传递给处理程序并让它调用正确的实例

解决方案(1)会比你以前更糟糕:现在你需要为每个块创建一个全新的类try并覆盖一堆方法以最终得到比你以前更糟糕的结果(目前还不清楚代码是如何在一个特定的类适合您的程序流程)。它还会留下另一个未回答的重要问题:您可能需要上下文(访问当前范围内的变量)来正确处理异常。您将如何提供对该上下文的访问?

解决方案 (2) 实际上最终会与编写catch我们一直想要避免的块非常相似(每个块Action实际上都是一个catch块的内容)。我们最终会做同样的事情,只是以更复杂和冗长的方式。

还有其他问题:

  • ExceptionHandler如果不能处理异常怎么办?再次抛出它会导致您丢失原始堆栈跟踪,实际上会破坏那里的所有有用信息。
  • 万一有bugExceptionHandler怎么办?您可以使用try/ catch。你能相信你自己写的代码到同样的程度吗?

至于ExceptionThrower……它可能提供什么好处throw new Exception();

异常处理已经是一件复杂的事情,如果不给机器增加额外的齿轮就很难做到正确。特别是如果他们不给你买任何新东西。不要这样做。

于 2011-04-06T22:48:33.920 回答
1

抱歉,这不是一个好主意。当您使用围绕相关部分的普通 try/catch 块在代码中捕获异常时,您可以使用两个关键信息来处理问题:异常类型以及异常发生的位置。

通过您的安排,您必须处理所有异常,只知道它们是什么类型的异常。您不再知道异常实际发生在哪里,因此除了记录它或向用户显示消息之外,您真的无法对问题做任何事情。

此外,try/catch 块通常还包括一个 finally 块,即使抛出异常(如关闭流等),您也可以在其中确保事情发生。你没有办法处理这个问题。

正确的异常处理可能很棘手,并且没有灵丹妙药可以让它变得简单明了。如果有的话,.Net 应该已经包含了它。

于 2011-04-06T22:50:47.670 回答
1

我们的代码库中有一个类,它的签名与您提出的那个非常相似,我现在可以告诉您,它只有粗略的痛苦和痛苦!

为什么你的代码中有这么多的 try-catch 块?你能举一些例子吗?异常本质上是“异常”,即不那么频繁!您不仅不应该经常捕获异常,而且每个异常都是不同的,并且在一种情况下工作的相同样板代码可能不适用于许多其他情况。

没有人说异常处理很容易(或生成紧凑的代码)——您应该仔细考虑需要捕获异常并适当处理它的每种情况——避免捕获不需要处理的异常。

于 2011-04-06T22:58:34.533 回答