【问题标题】:An Exception Handling Class异常处理类
【发布时间】:2011-04-06 22:39:47
【问题描述】:

在不必将 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 块,并让异常类处理其他所有事情,包括重新抛出、处理、记录、任何。

想法?

【问题讨论】:

  • 通过在 ExceptionHandler 类中重新抛出异常,您将丢失之前的堆栈跟踪。
  • 有没有办法保留堆栈跟踪?
  • throw;替换throw e;
  • 您可以返回bool 来判断Exception 是否被处理,而不是输入ExceptionHandler。如果不是,请使用原始代码中的“throw;”。但是,总的来说,我不确定这是一个好的设计。

标签: c# exception-handling try-catch rethrow


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    但接下来呢?

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

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

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

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

    还有其他问题:

    • 如果ExceptionHandler 无法处理异常应该怎么办?再次抛出它会导致您丢失原始堆栈跟踪,实际上会破坏其中的所有有用信息。
    • 如果ExceptionHandler 中有错误怎么办?您可以使用try/catch。你能相信你自己编写的代码吗?

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

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

    【讨论】:

    • 以一个简单的控制台应用程序为例——如果我在执行 UI 的 Main() 方法中有一行代码,并将 try catch 包裹起来,应用程序中不会出现任何异常冒泡到那个捕捉块?还是我错过了什么?为什么我需要更多的尝试/捕获?
    • @SeanThoman:因为有一些你可以合理预期的异常(例如,尝试打开一个文件你可能会得到一个FileNotFoundException)和意味着整个过程结束的异常(例如ExecutionEngineException) .您不能从同一个地方合理地回应两者;你需要不同的上下文。
    • 我想你可以只配置处理类的逻辑,但是它可能太复杂了,并且像你提到的那样引入它自己的错误。但是,如果这是一个非基于 UI 的应用程序会怎样,这会使它作为一种解决方案更可行吗?只是一个工作应用程序按顺序执行一些进程,完全不干涉,管理员只需定期检查日志以确保事情顺利进行。您不希望应用因某些异常等而停止运行。
    • @SeanThoman:暂时不要再考虑它是否可行,想想它能给你带来什么你已经无法做到的事情?“让某些东西发挥作用”很容易" 在一个 10 行的例子中。但作为一种通用工具,您自然希望在大型应用程序中使用它,这会增加而不是减少您的问题。
    • 好吧,对这个很新,感谢它!我看到了你的观点。我考虑的优点是它可以让你在一个地方精确地配置你的异常处理。您可以在一个相当解耦的组件中修改异常处理的逻辑。但也许这些“专业人士”即使是专业人士也被劣势所抵消。
    【解决方案3】:

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

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

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

    正确的异常处理可能很棘手,没有什么灵丹妙药可以让它变得简单明了。如果有,.Net 早就将其合并了。

    【讨论】:

    • 你可以通过使用堆栈跟踪知道它发生在哪里......但我确实明白你的意思。由于使用'throw e'会破坏堆栈跟踪,并且只允许在catch块中使用'throw',这似乎很棘手......你必须在'throw'之前记录堆栈跟踪或以编程方式对堆栈跟踪做一些事情e'。
    • 您的意思是,如果您编写了堆栈跟踪解析器(耶!),您可以推断出异常发生的位置。是的,但是您仍然需要将代码放在某处,以便适当地响应特定的异常类型和位置信息。为什么不把它放在一个正常的 catch 块中,就在导致问题的代码下方?
    【解决方案4】:

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

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

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

    【讨论】:

    • 我实际上并没有那么多 try catch 块......但我对这个想法很好奇。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 2011-07-02
    • 2011-01-25
    相关资源
    最近更新 更多