如何在c#/。net中记录引发的异常


139

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

我想提供良好的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>

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

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

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


4
我知道这并不是您要问的(这是一个非常老的问题),但是Eric Lippert(Microsoft C#编译器和设计团队的主要开发人员)写了一篇博客文章,介绍了我认为每个开发人员都可以使用的4种异常类型应约而写异常处理代码的思考:blogs.msdn.com/b/ericlippert/archive/2008/09/10/...
javajavajavajavajava

@javajavajavajavajava感谢您的链接-绝对值得一读。
阿诺德·索卡斯

我认为这是一个有效的问题,因为在C#中如何正确记录异常根本不明显,而50K视图表明对于许多人来说也不明显。第二个投票最多的答案非常有用,因为它表明使用现有的xmldocs来对此进行记录。投票重新开放。这种“基于观点”的紧密原因正在杀死许多实际上非常有用的编程问题。
Alexei

Answers:


110

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

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

您可能想要这样做的另一个地方是,如果您的方法在API的表面上。就像外观将多个接口简化为一个接口一样,您的API应该将多个异常简化为一个异常。使调用者更容易使用代码。


为了回答安德鲁的一些担忧(从评论中得出),有三种例外类型:您不知道的例外,您知道但无能为力的例外,以及您知道并且可以做某事的例外。

那些你不知道的人想要放手。快速失败的原理是-使您的应用程序崩溃比进入可能最终破坏数据的状态更好。崩溃将告诉您发生了什么情况以及原因,这可能有助于将该异常移出“您不知道的异常”列表。

您所知道但无能为力的是诸如OutOfMemoryExceptions之类的异常。在极端情况下,您可能希望处理此类异常,但是除非您有一些非常出色的要求,否则您将它们像第一类一样对待-放手吧。您是否需要记录这些例外?在新创建对象的每个方法上,记录OOM看起来都非常愚蠢。

您知道并可以做的事情就是应该记录和包装的内容。

您可以在此处找到有关异常处理的更多准则。


3
我必须承认这听起来不太实用。我无法想象我可能会调用的任何代码会引发多少潜在的异常,再加上像OutOfMemoryException这样的东西,您不想捕获和包装。
Andrew Hare

3
您的答案会好吗,但实际上是两个相互矛盾的答案。“记录可能由您的代码引发的每个异常”和“您知道并且可以做的事情就是您应该记录的那些异常”。
tymtam

2
@Tymek:不。上半部分回答了“我应该如何记录例外”问题,第二部分指出了对“我应该记录什么例外”的明显的答案。首先并不意味着您记录了所有可能发生的异常。有些人太过直白,需要下半场。

5
@Tymek我认为您的意思可能是,如果您可以对此做点什么,为什么不对它做点什么而不是将其扔掉并进行记录呢?说“您所知道的客户机代码可以做些事情的人” 可能更为真实。这消除了矛盾,因为这些是文档的理想例外。
莫。

至于您“放手”的例外,您总是可以将它们记录在较低级别上以将其记录下来。你懂; 只是以一种用户友好的方式使程序崩溃。
Nyerguds 2014年

96

您应该使用标准的xml文档

/// <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,则此文档可在智能提示中找到,并且可以在以后提醒您(或其他人)您可能会想到的异常。

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


1
如何增加价值?例如,所有这些异常都是Exception类型的派生。根据我的经验,考虑在方法中调用的其他API可能抛出的所有其他异常类型将是不切实际的。我的观点是,与携带任何业务信息的异常相比,我们不必担心方法抛出的异常。
Illuminati

7
@ShiranGinige您的经历是错误的。
Grozz

35

您可以通过使用几个出色的加载项来简化文档编制过程。其中之一是GhostDoc,它是Visual Studio的免费加载项,可生成XML文档注释。另外,如果您使用ReSharper,请查看出色的ReSharper 特工Johnson Johnson插件,它添加了一个选项,可以为抛出的异常生成XML注释。

更新:似乎Agen Johnson不适用于R#8,签出ReSharper的Exceptional作为替代方案...

步骤1:GhostDoc生成XML注释(Ctrl-Shift-D),而ReSharper的Agent Johnson插件建议也记录该异常:

第1步

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

第2步http://i41.tinypic.com/osdhm

希望有帮助:)


tinypic链接已断开。
ANeves

11

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

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

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

关于这方面的更多细节,也许您可​​以捕获并抛出自己的自定义异常?


4

您所用方法的合同的一部分应是检查前提条件是否有效,因此:

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 可能引发的异常等。


1
不知道该检查的意义是如果您只是要复制Open调用仍然会抛出的异常(更不用说,正如您所注意到的那样,这是一场竞赛,并且检查不能保证成功Open)。 。
马特·恩莱特

1
@MattEnright授予,但是我已经做了一些说明性的说明……
Rowland Shaw

1

您应该记录方法可能抛出的所有异常。

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

如果您无法处理或解决异常,则可以考虑对它们进行重新设计。通常打包/包装在对调用者而言更有意义的异常中。


1

确实,正如已经回答的那样,记录异常的方法是使用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 -注释-异常-II.aspx

问候。


0

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

请记住,这是要告知呼叫者期望的内容,以便他们可以选择如何处理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.