【发布时间】:2014-01-07 02:21:49
【问题描述】:
传统观点认为我们应该只捕获我们期望的特定异常类型:
try
{
int.Parse(str); //I am aware of TryParse(), this is just for the sake of example
}
catch (FormatException ex)
{
Console.WriteLine (ex);
}
catch (OverflowException ex)
{
Console.WriteLine (ex);
}
但是,有时我们并不真正关心发生了哪个异常(只要它不是致命的),可能是因为我们只是想让用户知道发生了错误。在这些情况下,我们可以这样做:
try
{
Foo();
}
catch (Exception ex)
{
if (IsCriticalException(ex))
{
Environment.FailFast("System Error", ex);
}
Console.WriteLine ("Error running Foo: {0}", ex);
}
IsCriticalException 的实现方式与System.ClientUtils.IsCriticalException 类似。出于以下几个原因,我通常赞成这种方法:
- 有些方法可以抛出许多异常类型,捕获它们可能很麻烦。例如,File.Open 可以抛出九种不同类型的异常。
- 某些方法可能会引发未记录的异常。这要么是由于缺少文档,要么是由于某些错误。例如,ICommunicationObject.Close 实际上可以抛出
ArgumentException,这是由于一个模糊的错误。有些方法甚至不能静态地知道它们会抛出哪些异常,因为它们会动态加载其他模块/插件。假设他们可以用他们自己已知的异常来包装所有这些“外部”异常,但我相信并不是所有的都这样做。
这种方法的批评者认为方法的异常是其契约的一部分。如果抛出不属于本合约的异常,我们应该假设它的状态是损坏的。我们还将在该方法中发现一个错误(否则我们不会发现一个错误,因为我们已经吞下了意外的异常)。然后我们可以将此错误传递给框架的开发人员,这是一个胜利 - 特别是如果这些开发人员在我们公司,所以我们可以说是“自助”了。
我承认,批评者提出了正确的观点,但我觉得他们有点理想主义。实际上,我认为通用的非致命异常捕获在很多情况下都是有意义的。我说得有道理吗?
相关阅读:
【问题讨论】:
-
你的
File.Open例子是一个坏例子,IMO。从ArgumentException派生的任何内容都指向您应该在首先调用File.Open之前验证的内容。PathTooLongException、DirectoryNotFoundException、FileNotFoundException都派生自IOException,并且可以使用单个 catch 块进行处理。只剩下三种类型需要担心:IOException、UnauthorizedAccessException、NotSupportedException而不是九种。 -
至于
ICommunicationObject.Close,我个人的偏好是将ArgumentException添加到要捕获的异常列表中(如果您知道它永远不应该被抛出,并且它只是因为错误而被抛出,并且该错误的影响使异常可以安全地忽略)。 -
@OhadSchneider 对我来说听起来很明智,我同意。这显然不是一刀切的方法。
-
@OhadSchneider "我为什么要检查字符串的长度、空格和无效字符?"一般来说,
ArgumentException和派生的异常意味着该方法告诉您“不要传递给我”,如果您仍然传递它,那么您就是代码有错误的人。我认为如果File.Open会为无效的文件名字符抛出不同的异常可能会更好。 -
@close-voters 我认为这是一个合理的设计问题,我是从现实生活中出现的问题中提出的。如果您不同意,请发表评论。
标签: c# .net exception-handling