【问题标题】:How to document thrown exceptions in c#/.net如何在 c#/.net 中记录抛出的异常
【发布时间】:2010-10-02 10:57:48
【问题描述】:

我目前正在编写一个小型框架,供公司内的其他开发人员在内部使用。

我想提供良好的 Intellisense 信息,但我不确定如何记录引发的异常。

在以下示例中:

public void MyMethod1()
{
    MyMethod2();

    // also may throw InvalidOperationException
}

public void MyMethod2()
{
    System.IO.File.Open(somepath...); // this may throw FileNotFoundException

    // also may throw DivideByZeroException
}

我知道记录异常的标记是:

/// <exception cref="SomeException">when things go wrong.</exception>

我不明白的是如何记录由代码调用MyMethod1()引发的异常?

  • 我应该记录MyMethod2() 抛出的异常吗?
  • 我应该记录File.Open() 引发的异常吗?

记录可能的例外情况的最佳方法是什么?

【问题讨论】:

  • 我知道这不是您要问的(这是一个非常古老的问题),但 Eric Lippert(微软 C# 编译器和设计团队的首席开发人员)写了一篇关于 4我认为每个开发人员在编写异常处理代码时都应该考虑的异常类型:blogs.msdn.com/b/ericlippert/archive/2008/09/10/…
  • @javajavajavajavajava 感谢您的链接 - 绝对值得一读。
  • 我认为这是一个有效的问题,因为在 C# 中如何正确记录异常并不明显,而且 50K 视图表明这对很多人来说也不明显。第二个投票最多的答案非常有帮助,因为它表明使用现有的 xmldocs 来记录这一点。投票重新开放。这种“基于意见”的密切原因正在扼杀许多实际上非常有用的编程问题。

标签: c# .net documentation intellisense


【解决方案1】:

您应该记录您的方法可能引发的所有异常。

为了隐藏实现细节,我会尝试自己处理 MyMethod2 的一些异常。

如果您无法处理或解决异常,您可以考虑追溯它们。主要是打包/包装在对调用者更有意义的异常中。

【讨论】:

    【解决方案2】:

    在您的方法中记录预期的异常,在您的示例中,我会让用户知道该方法可能会引发文件未找到异常。

    请记住,这是通知调用者预期的结果,以便他们选择如何处理。

    【讨论】:

      【解决方案3】:

      据我了解,使用 元素的目的是在装饰方法时使用它,而不是异常:

      /// <summary>Does something!</summary>
      /// <exception cref="DidNothingException">Thrown if nothing is actually done.</exception>
      public void DoSomething()
      {
      // There be logic here
      }
      

      应该在这些方法中捕获、处理和记录可能被其他调用方法抛出的异常。应该记录可能由 .NET 引发的异常,或由您自己的代码显式引发的异常。

      至于更具体,也许您可​​以捕获并抛出您自己的自定义异常?

      【讨论】:

        【解决方案4】:

        您应该使用standard xml documentation

        /// <exception cref="InvalidOperationException">Why it's thrown.</exception>
        /// <exception cref="FileNotFoundException">Why it's thrown.</exception>
        /// <exception cref="DivideByZeroException">Why it's thrown.</exception>
        public void MyMethod1()
        {
            MyMethod2();
            // ... other stuff here
        }
        
        /// <exception cref="FileNotFoundException">Why it's thrown.</exception>
        /// <exception cref="DivideByZeroException">Why it's thrown.</exception>
        public void MyMethod2()
        {
            System.IO.File.Open(somepath...);
        }
        
        /// <exception cref="FileNotFoundException">Why it's thrown.</exception>
        public void MyMethod3()
        {
            try
            {
                MyMethod2();
            }
            catch (DivideByZeroException ex)
            {
                Trace.Warning("We tried to divide by zero, but we can continue.");
            }
        }
        

        这样做的价值在于您提供了可能发生的已知异常的文档。如果您使用的是 Visual Studio,则可以在智能感知中获得此文档,并且可以稍后提醒您(或其他人)您可能会遇到的异常。

        您要指定具体的异常类型,因为您可能能够处理一种类型的异常,而其他类型是严重问题的结果,无法纠正。

        【讨论】:

        • 如何增加任何价值?例如,所有这些异常都是 Exception 类型的派生。根据我的经验,考虑可能从您的方法中调用的其他 API 抛出的所有其他异常类型是不切实际的。我的观点是,我们不应该担心从方法中抛出的任何异常,而不是那些携带任何业务信息的异常。
        • 链接已损坏。
        【解决方案5】:

        您应该记录您的代码可能引发的每个异常,包括您可能调用的任何方法中的异常。

        如果列表有点大,您可能想要创建自己的异常类型。捕获您在方法中可能遇到的所有问题,将它们包装在您的异常中,然后将其抛出。

        您可能希望这样做的另一个地方是,如果您的方法在您的 API 上。就像外观将多个接口简化为一个接口一样,您的 API 应该将多个异常简化为一个异常。让调用者更轻松地使用您的代码。


        为了回答 Andrew 的一些担忧(来自 cmets),存在三种类型的例外:您不知道的例外、您知道但无能为力的例外以及您知道但可以做的例外关于。

        那些你不知道你想放手的人。它的原则是快速失败——最好让你的应用程序崩溃而不是进入可能最终破坏数据的状态。崩溃会告诉您发生了什么以及原因,这可能有助于将该异常从“您不知道的”列表中移出。

        您知道但无能为力的是OutOfMemoryExceptions 之类的异常。在极端情况下,您可能希望处理这样的异常,但除非您有一些非常显着的要求,否则您将它们视为第一类——让他们去吧。您必须记录这些例外情况吗?在新建对象的每个方法上记录 OOM 看起来很愚蠢。

        那些你知道并能做些什么的就是你应该记录和包装的那些。

        你可以找到更多guidelines on exception handling here.

        【讨论】:

        • 我必须承认这听起来不太实用。我无法想象我可能调用的任何代码都会引发多少潜在的异常,而且还有像 OutOfMemoryException 这样的东西你不想捕获和包装。
        • 你的答案好不好,但实际上是两个相互矛盾的答案。 “记录你的代码可能引发的每一个异常”和“你知道并且可以做一些事情的那些是你应该记录的”。
        • @Tymek:不。前半部分回答了“我应该如何记录异常”的问题,第二部分指出了“我应该记录哪些异常”的明显答案。第一个并不意味着您记录了所有可能发生的异常。有些人太文字化了,这就需要下半场了。
        • @Tymek 我认为您的观点可能是,如果您可以对此做点什么,为什么不做点什么而不是重新抛出并记录它呢?说“那些你知道客户端代码可以做某事的人”可能更真实。这消除了矛盾,因为这些是记录的理想例外。
        • 至于你“放手”的异常,你总是可以在记录它们或其他东西的较低级别上捕获它们。你懂的;只是以一种用户友好的方式让程序崩溃。
        【解决方案6】:

        您的方法的部分合同应该是检查先决条件是否有效,因此:

        public void MyMethod2()
        {
            System.IO.File.Open(somepath...); // this may throw FileNotFoundException
        }
        

        变成

        /// <exception cref="FileNotFoundException">Thrown when somepath isn't a real file.</exception>
        public void MyMethod2()
        {
            FileInfo fi = new FileInfo( somepath );
            if( !fi.Exists )
            {
                throw new FileNotFoundException("somepath doesn't exists")
            }
            // Maybe go on to check you have permissions to read from it.
        
            System.IO.File.Open(somepath...); // this may still throw FileNotFoundException though
        }
        

        使用这种方法,可以更轻松地记录您明确抛出的所有异常,而不必同时记录 OutOfMemoryException 可能被抛出等。

        【讨论】:

        • 如果您要复制 Open 调用无论如何都会抛出的异常,不确定该检查的意义是什么(更不用说,正如您所指出的那样,有一场比赛和检查并不能保证Open) 的成功...
        • @MattEnright 是的,但我做了一些人为的说明来说明这一点......
        【解决方案7】:

        您可以通过使用几个出色的插件来简化文档过程。其中之一是GhostDoc,它是 Visual Studio 的免费插件,可生成 XML-doc cmets。此外,如果您使用 ReSharper,请查看 ReSharper 的出色 Agent Johnson Plugin,它添加了一个选项来为抛出的异常生成 XML cmets。

        更新:似乎 Agen Johnson 不适用于 R# 8,请查看 Exceptional for ReSharper 作为替代方案...

        第 1 步:GhostDoc 生成 XML 评论 (Ctrl-Shift-D),而 Agent Johnson 插件 对于 ReSharper 建议记录 例外:

        第 2 步:使用 ReSharper 的快捷键 (Alt-Enter) 添加例外 文档:

        step 2 http://i41.tinypic.com/osdhm

        希望有帮助:)

        【讨论】:

        • 小图片链接已损坏。
        【解决方案8】:

        确实,正如已经回答的那样,记录异常的方法是使用 XML 注释。

        除了插件之外,您还可以使用可与 TFS 集成的静态分析工具,以确保记录异常。

        在下面的链接中,您可以看到如何为 StyleCop 构建自定义规则,以验证您的方法引发的异常是否被记录在案。

        http://www.josefcobonnin.com/post/2009/01/11/Xml-Documentation-Comments-Exceptions-I.aspx http://www.josefcobonnin.com/post/2009/01/15/Xml-Documentation-Comments-Exceptions-II.aspx

        问候。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-02-12
          • 2011-02-22
          • 2021-04-23
          • 1970-01-01
          • 1970-01-01
          • 2014-03-31
          • 2016-05-30
          相关资源
          最近更新 更多