3

从库中冒泡异常的最佳方法是什么?当我实现一个接口时,用与我的实现细节有关的异常来调试调用方是好还是坏的做法?我有以下实现,其中每个实现错误都隐藏在调用方之外。在我看来,它是实现关注点分离的最干净的方式。但是我到处都读到你需要冒泡未触及的异常。

public class OneException : Exception
{
    public OneException()
    {
    }

    public OneException(string message): base(message)
    {
    }

    public OneException(string message, Exception innerException)
      : base(message, innerException)
    {
    }
}

我的库实现:

public class MyLib : IMyLib
{
    public int Divide(int a, int b)
    {
        try
        {
            if (b == 1) throw new OneException();
            return a / b;
        }
        catch (OneException e)
        {
            throw new ApplicationException("Please do not divide by 1", e);
        }
        catch (Exception e) // divide by zero and others
        {
            throw new ApplicationException("Oops", e);
        }
    }
}
4

7 回答 7

3

为了与单一职责原则保持一致,您应该注意您的库应该做的事情(如果可能的话)。否则,你应该让调用者处理它。

在您的示例中,您正确地抛出了一个被零除的异常。处理这不是您的图书馆的责任(除非您将其宣传为“安全”的划分方法)。该错误是由不正确的客户端输入引起的,因此应该由客户端来处理它。

但是,您的 OneException 将是一个不寻常的异常抛出。为什么客户端不能除以一?它当然不会造成任何伤害。这大概是一个人为的示例,但除非确实存在无法在内部解决的问题,否则您不应抛出异常。如果有人想除以一,为什么不允许他们这样做?

良好的异常处理将通知客户其输入和/或系统故障的问题,而不是您的逻辑错误。

于 2012-06-13T07:49:07.913 回答
2

选择正确的异常冒泡策略取决于两个主要因素:

  1. 您想尽可能多地向调用者提供有关异常上下文的详细信息吗?
  2. 你想对调用者隐藏敏感的异常数据吗?

如果您想提供有关异常上下文的更多详细信息,将异常包装到一些更具体的异常中是一个不错的策略。这里最明显的例子是业务异常

如果要对异常隐藏敏感数据,可以考虑替换异常。这种方法通常用于服务中,当调用方不受信任时。

注意:你不应该“吞下”异常,除非它不是正常程序流程的一部分。

于 2012-06-13T19:00:54.883 回答
1

作为一般规则,只处理那些您可以处理的异常。

在您的库实现中,您捕获不同的异常OneException并将Exception其重新抛出为ApplicationException. 我认为这是一种不好的做法,因为您完全隐藏了原始错误的含义。

ApplicationException如果唯一冒泡的异常是泛型而没有给出原因的线索,那么使用您的库 Divide() 方法的客户端代码应该如何知道究竟发生了什么?

你在这里所做的是抛出一个异常,在同一个方法中捕获它并重新抛出一个不同的异常。

让异常冒泡到客户端代码,以便客户端可以自己决定它想如何处理它 - 或者是否完全处理。

于 2012-06-13T07:36:59.090 回答
1

这取决于。如果您想处理异常并由于某些业务规则引发自定义异常,请确保处理异常并记录它或引发新的自定义异常,就像在您的示例代码中一样,您正在为divide by 1.

另一个例子可能是,如果您在表中输入数据并且您的获取主键约束异常,您可以捕获异常并根据您的库规范引发您自己的自定义异常,或者您可以保留异常原样,以在调用代码中处理。

如果您要维护内部错误/异常日志,则在库中处理异常可能会有所帮助。您可以记录异常,然后将其抛出,以便它可以冒泡到调用代码。但是恕我直言,如果您既没有记录也没有引发自定义异常,最好保持异常不变,以便可以在调用代码中处理它

于 2012-06-13T07:37:31.523 回答
1

尝试记录异常,这始终是一个好习惯。通过这种方式,客户实际上会知道出了什么问题。

于 2012-06-13T07:39:20.823 回答
1

问自己这些问题:

  • 作为开发人员,您希望消费者(调用者)了解您的库的哪些信息?

  • 作为消费者,如果发生错误,你想要什么?

如果标准 .Net 的异常不足以达到目的,您应该只开始公开您自己的异常类型。这方面的一个示例是与 COM 相关的错误 - 您可能希望为特定的错误条件创建自定义异常,以避免抛出嵌入了特殊数字的 COMException。

我会说OneException在你的例子中是浪费时间 - 为什么作为标准异常抛出,捕获然后包装和重新抛出?为什么不一ApplicationException开始就扔一个?这接近于使用异常来控制程序流,这是一种反模式。

于 2012-06-13T07:39:58.043 回答
1

系统涵盖了这两种例外情况。对于 b = 1 的情况,使用 ArgumentOurOfRange 异常,对于 b = 0 的情况,使用 DivideByZeroException。因此,对于您非常简单的示例库,无需创建额外的异常(这不是一个好习惯)。立即抛出异常是需求问题。在大多数情况下(良好做法),您会抛出可由调用者使用 try/catch(/finally) 处理的异常。

于 2012-06-13T07:46:26.310 回答