【问题标题】:Defining exceptions that can be thrown from a toolkit API定义可以从工具包 API 抛出的异常
【发布时间】:2009-12-03 21:50:02
【问题描述】:

是否有从工具包 API 引发异常的最佳实践或行业标准?

面向用户的方法是否应该在某种CustomException 中捕获并包装Exception,以便用户只需要担心API 中出现的CustomExceptions?

或者只是让那些冒泡的惯例?

我们关心的是能够记录我们的 API 方法可能抛出的所有可能的异常。 (例如,如果我们的 API 方法调用 Stream.Write() 抛出 4 或 5 个异常,我们必须记录所有这些以及其他被调用方法可能抛出的异常。)

我们正在考虑做这样的事情:

public void customerFacingApiMethod(){
   try {
      //api functionality goes here
   } catch (Exception e) {
      throw new CustomException(e);
   }
}

【问题讨论】:

    标签: c# .net api exception


    【解决方案1】:

    在我看来,让 API 只抛出一种异常类型是个坏主意。不同异常的好处之一是您可以选择捕获不同类型的异常并以不同方式处理它们。将异常包装成一个单一的异常类型会移除该功能。

    在适当的情况下使用框架提供的异常类型,并在适当的情况下为特定情况创建自己的自定义异常类型。最重要的是,确保为每个方法记录它们可能抛出的异常。

    【讨论】:

    • 原则上我们同意。但是,如果我们正在进行的调用之一更改了它抛出的异常,那么现在我们的文档不同步了。或者,如果他们的文档不正确并且抛出了未记录的异常,该怎么办。
    • 如果如果如果如果。万一你死了怎么办,关键是你正在破坏异常的最大特征,那就是它们是有类型的。
    【解决方案2】:

    在抛出异常时,请确保面向用户的异常都与用户在您的工具包方面实际做错了什么相关。这可能意味着将不同的文件系统异常捕获或合并到一个异常中,但您不应该只是捕获异常并抛出一个新异常——这实际上并没有告诉工具包用户他们做错了什么。

    【讨论】:

      【解决方案3】:

      您应该遵循与 .NET Framework 相同的概念。您的用户已经在使用该框架,因此他们知道它是如何工作的并期望某些行为。

      • 如果来自客户端的参数无效(酌情使用ArgumentNullExceptionArgumentOutOfRangeException 等),则引发ArgumentException 派生的异常。
      • 针对 IO 错误引发 IOException 派生异常。

      等等……

      在您的文档中,明确说明公共 API 的先决条件,并说明在失败时将引发的异常(如 MSDN 所做的那样)。

      【讨论】:

        【解决方案4】:

        我不喜欢它。如果您将所有异常包装到 CustomException 中,那么如果不在对象内部搜索 InnerExcpetion(如果您的 API 还使用其他执行相同操作的 API,则可能是多个级别的深度)就很难捕获特定异常。如果有 OutOfMemoryException,我想知道而不必去寻找它。

        【讨论】:

          【解决方案5】:

          不要对所有人使用相同的例外。

          您需要不同的异常类型才能将一种情况与另一种情况分开。对于任何你让它向上流动直到通用异常处理程序块捕获它的东西,它看起来很k,但是你正在阻止任何其他代码恢复——在特定情况下操作的额外操作。

          您可以封装一组异常,前提是这些异常适合调用代码可以处理的特定场景。不要忘记一些框架异常已经很好地传达了这种情况。

          【讨论】:

            【解决方案6】:

            Fredrik Mork 所说的非常真实。我还想补充一点,您可以轻松记录使用 xml cmets(和 GhostDoc)引发的异常。

            因此,API 用户可以在文档中查找可以引发哪些异常。但是,也有缺点。

            如果您的 API 中有一个很深的调用堆栈,尤其是当圈复杂度变得很高(即可能的分支数量)时,您将无法恢复所有可能引发的异常(可能是由运行时引发的?)

            所以我建议只在无法恢复的情况下才抛出,并且只在 API 的上层抛出,即不超过 2 深。并捕获所有在调用堆栈中更深层次抛出的异常,并将它们作为 InnerException 或类似的东西返回。

            【讨论】:

              【解决方案7】:

              有两种类型的异常需要考虑(忽略 StackOverflowException 等,无论如何你都无能为力):

              1. 由外部问题(如文件 IO)引起的,必须处理。
              2. 通过正确验证的输入始终可以避免这些问题。

              将 IO 类型的异常不加修改地向上传递到调用堆栈通常是有意义的,因为无论如何您都无能为力。如果您的 API 在 GUI 级别运行(这是相当不寻常的),那么您可能会重试或询问用户要做什么的预期错误除外。

              请务必记录您的 API 方法可以抛出哪些此类异常。

              在第二种类型(总是可以防止的异常)中,这些异常可能由您的 API 在错误输入时生成,但绝不应从较低级别向上传递调用堆栈。应该验证您调用的任何内容的输入,以便不会发生这些错误,并且如果由于某种原因可能发生它们,那么应该包装这些异常。否则,使用您的 API 的代码将不得不处理错误抽象级别的异常。

              再一次,一定要记录什么样的输入不会产生异常,以及如果这些规则被破坏会出现什么样的异常。

              在所有情况下,抛出对 API 用户有意义的异常,而不是对 API 本身进行编程的人。

              【讨论】:

                【解决方案8】:

                在一般情况下捕获 System.Exception 的问题在于,这样做实际上是在说“我不知道为什么会失败,这没关系,因为我知道无论如何我都可以继续! "

                这很少,如果有的话,是真的。

                将所有异常封装在一个自定义异常类型中意味着您的消费者只会捕获该类型 - 这在道德上等同于捕获 System.Exception。虽然这样做可能会满足某些策略/FXCop 规则,但实际上您仍然只是在捕获 System.Exception。

                对于您的异常政策,我首先要问这个问题:“如果我不知道这个 API 实际处理的是什么,我希望得到什么样的异常?”请记住,方法是对对象做某事的请求,而例外是对象让您知道它不能做的方式,以及为什么

                【讨论】:

                  【解决方案9】:

                  如果您创建一个用于整个 API 的异常类型,则其含义对于遇到该异常类型的客户端应用程序将是模糊的。相反,请考虑为您的 API 创建一个异常类层次结构

                  为此,首先定义一个抽象的基本异常类型:

                  CustomException
                  

                  ...然后从中派生出更具体的异常类型。例如:

                  CustomFooException
                  CustomBarException
                  CustomFizException
                  CustomFuzException
                  

                  抽象基类型CustomException 永远不会直接从您的API 中抛出。但是,如果您的 API 客户端不关心从您的 API 引发的特定派生异常,则它可以捕获基本异常类型 CustomException

                  有关开发异常类层次结构的更多信息,请参阅this answer 和此问题的其他答案:Why create custom exceptions in .NET?

                  另请参阅this answer,其中介绍了将异常层次结构的基类抽象化的想法。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2011-05-30
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-10-06
                    • 2014-10-27
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多