异常处理类
本文关键字:异常处理 | 更新日期: 2023-09-27 17:49:33
处理异常而不必到处放置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();
}
}
我认为这将是一个有趣的尝试,因为理论上你只需要一个或非常少的try/catch块围绕你的main()方法调用,并让异常类处理其他一切,包括重新抛出,处理,日志记录,等等。
想法吗?
在产品代码中不看到类似的设计实际上是有一个很好的理由的。
首先,这样的设计不能帮助您减少代码中try
/catch
对的计数(这应该是显而易见的)。它可以帮助您减少给定try
的catch
语句的数量,因为您可以捕获System.Exception
并转发给ExceptionHandler
…
但是接下来呢?
每个异常都需要以不同的方式处理。ExceptionHandler
如何确切地知道该做什么?你可以尝试用多种方法来解决这个问题,例如:
- 继承
ExceptionHandler
,并将处理异常的代码放在虚拟方法中 - 传递一些
Action<Exception>
实例给处理程序,并让它调用适当的一个
解决方案(1)将比以前更糟糕:现在您需要为每个try
块创建一个全新的类,并覆盖一堆方法,最终得到比以前更糟糕的(无法立即清楚特定类中的代码如何适合您的程序流)。它还会留下另一个重要的问题没有回答:您可能需要上下文(访问当前作用域中的变量)来正确处理异常。您将如何提供对该上下文的访问?
解决方案(2)实际上与编写我们一直想要避免的catch
块非常相似(每个Action
将有效地成为catch
块的内容)。我们最终做了同样的事情,只是以一种更复杂和冗长的方式。
还有其他问题:
- 如果
ExceptionHandler
不能处理异常,应该怎么做?再次抛出它将导致你失去原来的堆栈跟踪,实际上破坏了那里所有好的信息。 - 如果
ExceptionHandler
有bug怎么办?您可以信任try
/catch
。你能同样信任自己写的代码吗?
对于ExceptionThrower
…它比throw new Exception();
有什么好处呢?
异常处理已经是一件复杂的事情,如果不给机器增加额外的齿轮,就很难把它处理好。尤其是他们不给你买新东西的时候。别这样。
好吧,这可能不是你想要的答案,但是…
我通常对通用异常处理类的想法很反感。你几乎可以听到它本身是一个矛盾。异常是一个异常事件。异常事件不能以一般方式处理,而是需要在它们出现的地方进行定制处理,这本质上意味着您的代码应该做两件事:
- 对任何输入进行防御,以避免首先出现异常
- 将
try..catch
块放在捕获和处理异常有意义的地方(注意,这意味着您不应该在所有方法中使用try..catch
块)
那么,捕获和处理异常在哪里有意义呢?简而言之,您的代码具有使其能够处理异常的知识。如果没有,让异常向上冒泡到调用者。only的地方我认为你应该捕捉所有异常并有一些通用的默认行为,它在你的应用的顶层,这通常是UI。
对不起,这不是一个好主意。当您在代码中使用围绕相关部分的正常try/catch块捕获异常时,您可以使用两个关键信息来处理该问题:异常的类型,以及异常发生的位置。
在你的安排下,你必须处理所有的异常,只知道它们是什么类型的异常。您不再知道异常实际发生在哪里,因此除了将其记录下来或向用户显示一条消息外,您实际上无法对问题做任何事情。
此外,try/catch块通常还包括finally块,在其中您可以确保即使抛出异常(如关闭流等)也会发生事情。在你的安排中,你没有办法处理这件事。
正确的异常处理可能很棘手,没有什么灵丹妙药能让它变得简单直接。如果有的话,. net早就把它合并了。
我们的代码库中有一个类,它的签名与您提出的签名非常相似,现在我可以告诉您,它只带来了痛苦和痛苦!
为什么你的代码中有那么多try-catch块?你能举几个例子吗?异常的本质是"异常",也就是说不那么频繁!你不仅不应该频繁地捕捉异常,而且每个异常都是不同的,在一种情况下工作的相同的样板代码可能不适用于许多其他情况。
没有人说异常处理是容易的(或产生紧凑的代码)-你应该仔细考虑关于你需要捕获异常的每种情况,并适当地处理它-避免捕获你不需要处理的异常。