我是否应该在我的方法中捕获“纯文档”目的的异常,从而将错误文档封装在方法本身中,或者是调用者的责任?
假设我在我的EncryptPackage()
方法中调用了许多其他方法,包括框架方法,这可能会引发许多异常。我将所有内容都包含在using
块中,因此不需要捕获清除异常(或者我使用try / finally进行清理)。我是否应该捕获异常,并提供有关该方法的上下文的详细信息,还是调用方法的责任?
以下是案例一:
[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());
}
}
}
在此代码中,请注意EncryptPackage()
方法如何捕获所有可能的异常,只是为了“装饰错误文本”,“包加密期间发生错误”文本。 EncryptPackage()
这里封装了错误描述逻辑。
这是另一种技巧:
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());
}
}
}
在此示例中,EncryptPackage()
没有捕获任何内容,因为调用程序无论如何都会记录错误情况“包加密期间发生错误。\ r \ n \ r \ n”消息。
请注意,这是一个非常简化的示例,在现实世界中将有许多层次类,并且异常将通过长调用堆栈传播,因此首选捕获异常的方法是什么?第二种方法看起来“更干净”,因为异常是在一个层中处理的,其中一些“实际处理”(例如显示给用户)将要发生。调用堆栈信息将保留在异常对象中,因此从技术上讲,可以找出抛出异常的确切位置。但是......这似乎并不像第一种方法那样“记录良好”,其中每个抽象级别都会将自己的描述添加到错误中,从而保留innerException
成员中的先前异常。在这种情况下,当执行离开TestClass
层时,它已经包含此类中发生的错误的详细描述。所以这感觉是错误处理逻辑的更好封装。
使用哪一个?
答案 0 :(得分:4)
有一个chapter on this in Effective Java:
较高层应该捕获较低级别的异常,取而代之的是 抛出可以用更高级别来解释的异常 抽象。这个成语被称为异常翻译。
答案 1 :(得分:2)
我更喜欢你的第二个例子,主要是因为它可以显着减少你必须编写的错误处理代码的数量,特别是如果你正在编写自定义异常 - 第一个例子你最终会得到很多自定义异常类不要带来太多好处(你已经有了调用堆栈来告诉你异常的来源)。
您可能认为拥有更具描述性的错误消息很好,但是谁从中受益?最终用户?您是否应该向用户显示异常消息(以及您要使用的语言)?很多时候用户只需要知道存在内部错误,他们应该放弃(重启),或者再试一次。您对开发人员有益吗?您可能最终会使用前面的源代码检查调用堆栈,因此您不需要更具描述性的消息,您可以自己查看代码在此处执行的操作。
这不是一个硬性规定。大多数情况下,我只捕获顶级异常,在那里我记录它们并向用户报告错误。如果您直接向用户报告异常,那么原始异常通常不会从转换中受益,例如,如果您尝试打开不存在的文件,那么System.IO.FileNotFoundException足够描述,那么为什么要将它转换为别的什么?你真的想做同样的判断电话(“我比图书馆作者更了解所以我要翻译他们精心设计的例外情况”),因为那里有所有的例外情况吗?如果我想从它们中恢复(通常不可能),我只捕获较低的异常,或者,很少,我想将它们转换为更具描述性的异常。
在分层架构中,在层之间转换异常是有意义的,例如,将来自数据访问层的异常捕获到适合应用层的形式,以及类似地在应用层和用户界面之间,但我不知道你是否在研究那种类型的系统。
如果要记录异常,则应使用xml文档中的exception tag来获取该方法。然后,可以将其用于文档中的常规帮助文件,例如,使用SandCastle。
答案 2 :(得分:1)
根据上面的@Sjoerd,转换异常,使它们处于相同的抽象级别。在您的情况下,EncryptPackage应该自己翻译任何较低级别的异常,而不是调用者。
假设较低级别的异常来自数据库层(比如DBException)。调用者是否希望了解DBException?答案是否定的:调用者想要包含一个包,而不是DBException。出于调试目的,应将较低级别的异常链接在较高级别的异常中。
最后,我知道TestClassException是一个例子,但要确保异常类清楚地描述了这个问题:我个人不喜欢乏味的泛型异常类(除了为其他异常创建一个公共基类)。
答案 3 :(得分:0)
你应该尝试/捕捉一些容易区分的情况:
任何可以“外部”调用的方法,例如应用的入口点,UI事件,多线程调用等。在每一个捕获物上放置一些日志输出或消息。这将阻止您的应用程序崩溃(大部分),并为您或用户提供有关如何解决问题的一些反馈。
什么时候才能真正处理异常。这意味着您的应用可以选择辅助数据库或服务器URL,应用其他处理等。
当您想要阻止某些可选项破坏主工作流程时,例如,无法删除临时文件不应导致您的进程失败。
可能还有其他一些地方你需要尝试/捕获但这些应该是罕见的
始终将错误处理与一种体面的日志记录和/或消息传递方式结合起来,不要让任何异常信息消失,因为这就是你获取应用程序的方式而且“不明显的原因”表现不佳 - 至少理由应该是显而易见的。
此外 - 请勿使用例外来控制您的工作流程。除非绝对没有别的办法,否则真的不应该有任何“抛出”。