简体   繁体   English

我是否应该在我的方法中捕获“纯文档”目的的异常?

[英]Should I catch exceptions in my method for “purely documenting” purposes?

Should I catch exceptions in my method for "purely documenting" purposes, thus encapsulating the error-documentation inside the method itself, or is that responsibility of the caller? 我应该在我的方法中捕获“纯文档”目的的异常,从而将错误文档封装在方法本身中,还是调用者的责任?

Suppose I call numerous other methods in my EncryptPackage() method, including the framework ones, which potentially throw numerous exceptions. 假设我在我的EncryptPackage()方法中调用了许多其他方法,包括框架方法,这可能会引发许多异常。 I wrap everything in using blocks, so no need to catch exceptions for cleanup (or I use try/finally for cleanup). using块包装所有东西,因此不需要捕获清理异常(或者我使用try / finally进行清理)。 Should I catch the exception anyway, and provide the details about the context of that method, or is it the responsibility of caller method? 我是否应该捕获异常,并提供有关该方法的上下文的详细信息,还是调用方法的责任?

Here is the case one: 情况如下:

[Serializable]
class TestClassException : Exception
{
    public TestClassException() : base() { }
    public TestClassException(string message) : base(message) { }
    public TestClassException(string message, Exception innerException) : base(message, innerException) { }
}

class TestClass
{
    public TestClass() { }

    public void EncryptPackage()
    {
        try
        {
            DoSomething();
            DoAnotherThing();
        }
        catch (Exception ex)
        {
            throw new TestClassException("Error occurred during package encryption", ex);
        }
    }
}

class ConsumerExample
{
    public ConsumerExample() { }

    public void DoSomeStuff()
    {
        TestClass testClass = new TestClass();

        try
        {
            testClass.EncryptPackage();
        }
        catch (TestClassException ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.ToString());
        }
    }
}

In this code, notice how the EncryptPackage() method catches all possible exceptions, just to "decorate the error text", with a "Error occurred during package encryption" text. 在此代码中,请注意EncryptPackage()方法如何捕获所有可能的异常,只是为了“修饰错误文本”,“包加密期间发生错误”文本。 EncryptPackage() here encapsulates the error-description logic. EncryptPackage()在这里封装了错误描述逻辑。

And here is another technique: 这是另一种技术:

class TestClass2
{
    public TestClass2() { }

    public void EncryptPackage()
    {
        DoSomething();
        DoAnotherThing();
    }
}

class ConsumerExample2
{
    public ConsumerExample2() { }

    public void DoSomeStuff()
    {
        TestClass testClass = new TestClass();

        try
        {
            testClass.EncryptPackage();
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show("Error occurred during package encryption.\r\n\r\n" + ex.ToString());
        }
    }
}

In this example, EncryptPackage() does not catch anything, because the caller documents the error case anyway with "Error occurred during package encryption.\\r\\n\\r\\n" message. 在此示例中, EncryptPackage()不会捕获任何内容,因为调用程序无论如何都会记录错误情况,并且“在程序包加密期间发生错误。\\ r \\ n \\ r \\ n”消息。

Please note that this is a very simplified example, in real world there will be numerous hierarchical classes, and exceptions will be propagating through the long call stack, so which method of catching exceptions is preferred? 请注意,这是一个非常简化的示例,在现实世界中将有许多分层类,并且异常将通过长调用堆栈传播,因此首选捕获异常的方法是什么? Second approach seems "cleaner", because the exception is handled in a layer where some "actual handling" (eg displaying to user) is going to take place. 第二种方法似乎“更干净”,因为异常是在一个层中处理的,其中一些“实际处理”(例如显示给用户)将要发生。 Call stack information would be preserved in exception object, so technically it will be possible to find out where exactly the exception was thrown. 调用堆栈信息将保留在异常对象中,因此从技术上讲,可以找出抛出异常的确切位置。 But... that does not seem as "well-documenting" as the first approach, where each level of abstraction adds its own description to the error, preserving the previous exception in an innerException member. 但是......这似乎并不像第一种方法那样“记录良好”,其中每个抽象级别都将自己的描述添加到错误中,从而在innerException成员中保留先前的异常。 In this case, when the execution leaves the TestClass layer, it already contains detailed description of the error that happened within this class . 在这种情况下,当执行离开TestClass层,它已经包含了这个类中发生的错误的详细说明。 So this feels to be the better encapsulation of error-handling logic. 所以这感觉是错误处理逻辑的更好封装。

Which one to use? 哪一个使用?

There is a chapter on this in Effective Java : 在Effective Java中有一章关于这一点

Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction. 较高层应该捕获较低级别的异常,并且在它们的位置抛出可以用更高级别抽象来解释的异常。 This idiom is known as exception translation. 这个成语被称为异常翻译。

I prefer your second example, mainly because it can signicantly reduce the amount of error handling code you have to write, especially if you are writing custom exceptions - with the first example you could end up with a lot of custom exception classes which do not give much benefit (you already have the call stack to tell you where the exception came from). 我更喜欢你的第二个例子,主要是因为它可以显着减少你必须编写的错误处理代码的数量,特别是如果你正在编写自定义异常 - 在第一个例子中你最终可能会得到许多自定义异常类好处(你已经有调用堆栈告诉你异常的来源)。

You might think it is nice to have a more descriptive error message, but who benefits from this? 您可能认为有一个更具描述性的错误消息很好,但是谁从中受益? The end-user? 最终用户? Should you even be displaying exception messages to your user (and what language are you going to use)? 您是否应该向用户显示异常消息(以及您要使用的语言)? A lot of the time the user just needs to know that there has been an internal error, and they should give up (restart), or try again. 很多时候用户只需要知道存在内部错误,他们应该放弃(重启),或者再试一次。 Do you the developer benefit? 您对开发人员有益吗? You are probably going to end up examining the call stack anyway with the source code in front of you, so you don't really need a more descriptive message, you can see for yourself what the code is doing at that point. 您可能最终会使用前面的源代码检查调用堆栈,因此您不需要更具描述性的消息,您可以自己查看代码在此处执行的操作。

This is not a hard and fast rule. 这不是一个硬性规定。 Most of the time I only catch exceptions at the top level, where I log them and report an error to the user. 大多数情况下,我只捕获顶级异常,在那里我记录它们并向用户报告错误。 If you are reporting the exception directly to the user, then often the original exception does not benefit from translation, eg, if you try to open a non-existent file, then the System.IO.FileNotFoundException is descriptive enough so why translate it to something else? 如果您直接向用户报告异常,那么原始异常通常不会从转换中受益,例如,如果您尝试打开不存在的文件,那么System.IO.FileNotFoundException足够描述,那么为什么要将它转换为别的什么? Do you really want to make the same judgement call ("I know better than the library author so I am going to translate their carefully crafted exceptions") for all of the gazillions of exceptions out there? 你真的想做同样的判断电话(“我比图书馆作者更了解所以我要翻译他们精心设计的例外情况”),因为那里有所有的例外情况吗? I only catch exceptions lower down if I want to recover from them (generally not possible), or, very rarely, I want to translate them to a more descriptive exception. 如果我想从它们中恢复(通常不可能),我只捕获较低的异常,或者,很少,我想将它们转换为更具描述性的异常。

In a layered architecture, it can make sense to translate exceptions between the layers, eg, catch exceptions coming out of the data access layer to a form suitable for the application layer, and similarly between the application layer and the user interface, but I don't know if you are working on that type of system. 在分层体系结构中,在层之间转换异常是有意义的,例如,将来自数据访问层的异常捕获到适合应用程序层的形式,以及类似于应用程序层和用户界面之间的异常,但我不喜欢不知道你是否正在研究那种类型的系统。

If you want to document your exceptions, you should use the exception tag in the xml documentation for the method. 如果要记录异常,则应在xml文档中使用该方法的异常标记 This can then be used to general help files from the documentation, eg, using SandCastle . 然后,这可以用于文档中的常规帮助文件,例如,使用SandCastle

As per @Sjoerd above, translate exceptions so they are in the same level of abstraction. 根据上面的@Sjoerd,转换异常,使它们处于相同的抽象级别。 In your case EncryptPackage should translate any lower-level exceptions itself, NOT the caller. 在您的情况下,EncryptPackage应该转换任何较低级别的异常本身,而不是调用者。

Say the lower-level exceptions were from a DB layer (say DBException). 假设较低级别的异常来自数据库层(比如DBException)。 Would the caller expect to understand DBException? 调用者是否希望了解DBException? The answer is NO: the caller wants to encrpt a package, not a DBException. 答案是否定的:调用者想要包含一个包,而不是DBException。 The lower-level exceptions should be chained INSIDE the higher-level exception for debugging purposes. 出于调试目的,应将较低级别的异常链接在较高级别的异常中。

Finally, I know TestClassException is an example, but make sure the exception class describes the problem clearly: I, personally, don't like bland, generic exception classes (except to make a common base-class for other exceptions). 最后,我知道TestClassException是一个例子,但要确保异常类清楚地描述了这个问题:我个人不喜欢平淡无奇的异常类(除了为其他异常创建一个公共基类)。

You should try/catch in few, easily distinguished situations: 您应该尝试/捕捉一些容易区分的情况:

  • any method that can be invoked "externally", such as your app's entry point, UI events, multi-threaded calls and others. 任何可以“外部”调用的方法,例如应用程序的入口点,UI事件,多线程调用等。 Put some log output or message on each and every catch you have. 在每一个捕获物上放置一些日志输出或消息。 This will prevent your app from crashing (for the most part) as well as provide you or the user with some feedback on how to fix the problem. 这将阻止您的应用程序崩溃(大部分),并为您或用户提供有关如何解决问题的一些反馈。

  • when you can really handle the exception. 什么时候你可以真正处理异常。 This means your app can, for example, opt for a secondary database or server URL, apply a different processing etc. 这意味着您的应用可以选择辅助数据库或服务器URL,应用不同的处理等。

  • when you want to prevent something optional from ruining the main workflow, for example failing to delete your temp file shouldn't cause your process to fail. 当您想要阻止某些可选项破坏主工作流时,例如,未能删除临时文件不应导致您的进程失败。

  • there are probably some other places where you'll need a try/catch but these should be rare 可能还有其他一些你需要尝试/捕获的地方,但这些应该是罕见的

Always combine error handling with a decent way of logging and/or messaging the user, don't let any exceptions info disappear because that's how you get apps and don't behave well for "no apparent reason" - at least the reason should be made apparent. 总是将错误处理与一个体面的日志记录和/或消息传递方式结合起来,不要让任何异常信息消失,因为这就是你获取应用程序的方式而且“没有明显的理由”表现不好 - 至少原因应该是显而易见的。

Also - don't use exceptions to control your workflow. 此外 - 不要使用例外来控制您的工作流程。 There really shouldn't be any "throw"s unless there's absolutely no other way of doing something. 真的不应该有任何“抛出”,除非绝对没有别的办法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM