嵌套的try-catch块有什么意义?

3
这里有一份伪代码:
try
{
  TextReader tr = new StreamReader("filename");
  try
  {
    string content = tr.ReadToEnd();
  }
  catch(Exception ex)
  { /*Show error message*/ }
  finally
  { tr.Close();}
}
catch(Exception ex)
{ /*Show error message*/ }

好的,这段代码是一本书的示例。我不明白为什么要使用嵌套的try-catch块。

如果

string content = tr.ReadToEnd();

如果出现错误,那么外部的catch应该捕获异常,对吗?所以我看不出在那一行使用额外的try-catch有任何意义!

如果您发现这里有任何误解,请指出来,谢谢!

此外,是否有任何情况下嵌套的try-catch是合理使用的?


要检测错误发生的时间,是在new StreamReader还是在ReadToEnd时? - khachik
@0A0D - 这怎么可能是一个关于PHP的重复问题? - Jeffrey L Whitledge
3
不是这样的。不同的编程语言在处理异常时有不同的方法。在你取消了C#标签之前,这是一个C#问题。 - Jeffrey L Whitledge
@0A0D:PHP 没有 finally 结构。 - BoltClock
@0A0D:绝对不行。汽车和卡车都有轮子这一事实并不能使“我应该买什么样的轮胎”成为一个“与车辆无关”的问题。在提出这种说法之前,请您先了解更多(几十种)语言中的异常处理。 - Derrick Turk
显示剩余5条评论
4个回答

3

你的示例与只有一个外部catch块的类似代码之间的区别:

1)如果内部块抛出异常,则在调用Close()之前会打印消息。如果Close()还包含日志记录,则可能很重要。

2)如果抛出并捕获内部异常,然后在finally块中Close() 抛出异常,则会产生两个异常并处理两个异常。如果只有一个catch块,则如果Close()抛出异常,则下一步会发生什么可能取决于语言,但您不会收到两个错误消息。我希望第二个异常能够替换第一个异常,但我不保证这是所有语言(甚至是我使用过的所有语言...)中finally的情况。

3)//显示错误消息只是一个注释,它什么也不做。作者的意图可能是您在两种不同情况下显示不同的错误消息。内部消息将说“读取失败”,而外部消息将说“无法打开文件”。或者类似于那样的东西。要使用一个catch块实现这一点,您可以设置和检查指示进度的标志(这可能不会使代码比两个catch块更简单),或者您可以依赖异常本身包含适当的错误消息(在这种情况下,祝您好运将本地化框架与内置或第三方库中抛出异常的函数集成)。

请注意,即使只有一个catch,由于finally,您仍然需要两个try。这不好:

try {
    TextReader tr = new StreamReader("filename");
    string content = tr.ReadToEnd();
} catch(Exception ex) {
    // show error message
} finally {
    tr.Close();
}

从这种语言的外观来看,trfinally块中不会被识别,因此我们无法在那里关闭它。我们必须在处理创建它的块内部关闭它。也许我们可以像这样处理它:

TextReader tr = null;
try {
    tr = new StreamReader("filename");
    string content = tr.ReadToEnd();
} catch(Exception ex) {
    // show error message
} finally {
    if (tr != null) {
        tr.Close();
    }
}

但是与原始代码相比,这并没有真正简化事情,我们仍然需要处理2-3的差异,而现在我们根本不处理Close()引起的异常。因此,对于许多目的来说,具有多个try/catch的原始代码更好。


我认为重要的是第二点。外部try catch捕获new StreamReader时的异常,例如FileNotFound等,并且它还封装了tr.close()上的异常。这使得内部try catch仅处理读取错误,而不是“连接”类型错误。 - FoneyOp
@FoneyOp:也许吧,虽然很难通过示例代码判断它应该说明什么,这就是为什么我提到了所有可能的情况。当我读取流时,我通常不会太担心 Close() 会抛出异常,所以我不确定它在这里是否非常重要。如果我正在编写代码,Close() 上的错误确实变得非常有趣,因此也许这段代码旨在为我们一般处理流做好准备。 - Steve Jessop
同意。或者可能是数据库连接等。 - FoneyOp

2

举个例子,当我需要处理特定类型的异常时:

    try
    {

           //...
           //IMPORTANT CODE HERE
           //....
        try
        {
            // Read in non-existent file.
            using (StreamReader reader = new StreamReader("not-there.txt"))
            {
            reader.ReadToEnd();
            }
        }
        catch (FileNotFoundException ex)
        {
           //Do something important here
        }
       //...
       //MORE IMPORTANT CODE
       //...
    }
    catch(Exception ex) ///An Exception I'm not expecting
    { //oops...handle gracefully here
 }

2

如果 string content = tr.ReadToEnd(); 出现问题,那么外部的 catch 应该捕获异常,对吗?

不是的,内部的 catch 将会捕获异常并显示错误消息。然后内部的 finally 会被执行。然后外部的 try 块没有任何异常需要处理,因为内部的 try-catch 块已经处理了它。


1

你希望尽早捕获异常。让事情冒泡到外部异常是不好的实践。记住这个规则:尽早失败,经常失败。


3
我不同意这个作为普遍规律。如果在不同情况下可以有用地执行不同的操作,那么你应该这样做。如果你无论如何都会做同样的事情,那么不让问题上升到代码中最能处理错误的点是一种不好的实践。 "尽早失败"意味着尽快抛出异常,但是在能够对其进行有用处理之前,你绝不能捕获异常。 - Steve Jessop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接