【问题标题】:Finally block in try/catch not working?最后阻止 try/catch 不起作用?
【发布时间】:2014-04-17 19:32:23
【问题描述】:

好的,据我了解,try/catch 块尝试操作,catch 块捕获异常。越是特定的异常出现在顶部,越是通用的捕获块系列的底部。在下面的代码中,我实现了 try/catch,一切正常。

据我所知,finally 块总是会执行。有些人认为 finally 阻塞没有意义,因为如果有异常或没有异常,最后一个 catch 块之后的代码无论如何都会被执行。

但是,反对这一点的论点是,如果 在 catch 块中 抛出异常,则没有后续的 catch 块来捕获该异常。因此,通过将资源清理代码放在 finally 块中,您可以确保在 catch 块中引发异常时释放资源。

这就是为什么下面的代码让我感到困惑的原因。我在第一个 catch 块中抛出了一个异常,而 finally 块永远不会执行。为什么?

*请注意,创建 myStreamReader 时确实引发了异常,因为该文件实际上称为 generic.txt,并且故意拼写错误,以便引发初始异常。

StreamReader myStreamReader = null;

try
{
   myStreamReader = new StreamReader("c:\\genneric.txt");   
   Console.WriteLine(myStreadReader.ReadToEnd());       
}

catch(FileNotFoundException Error)
{
   Console.WriteLine(Error.Message);
   Console.WriteLine(); 
   throw new Exception();
}

catch(Exception Error)
{
   Console.WriteLine(Error.Message);
   Console.WriteLine();
}

finally
{

  if(myStreamReader != null)
  {
    myStreamReader.Close();
  }

  Console.WriteLine("Closed the StreamReader.");
}

视频:

此代码块的问题源于此视频,时间为 27:20:

https://www.youtube.com/watch?v=WxdSb3ZCWYc&list=PLAC325451207E3105&index=41

这家伙直接声明在catch块中发生的异常不会阻止finally块的执行。我看到确实如此。

【问题讨论】:

  • 不要听这些人告诉你没有目的,他们很可能没有正确地做到这一点。通常还建议仅使用您可以处理的异常。如果发生未处理的异常,finally 也会运行。
  • 嗯,我同意你的第一句话。但是,在这种情况下,finally 块似乎没有运行。
  • 我从来没有看到“关闭 StreamReader”的文字。所以很明显在第一个catch块中抛出了一个Exception,但是finally块没有执行。
  • @PrasanthVJ 很好的链接,但最重要的选择答案(+22)似乎表明 finally 块确实运行了。我不认为是这种情况。

标签: c# oop exception exception-handling


【解决方案1】:

如果该新异常完全未处理,则整个过程将被拆除,finally 块将永远无法运行。

如果有其他更高级别的异常处理程序,或者已安装未处理的异常处理程序,finally运行。


此示例确实显示“已关闭 StreamReader”:

    static void Main()
    {
        try
        {
            StreamReader myStreamReader = null;

            try
            {
                myStreamReader = new StreamReader("c:\\genneric.txt");
                Console.WriteLine(myStreamReader.ReadToEnd());
            }

            catch (FileNotFoundException Error)
            {
                Console.WriteLine(Error.Message);
                Console.WriteLine();
                throw new Exception();
            }

            catch (Exception Error)
            {
                Console.WriteLine(Error.Message);
                Console.WriteLine();
            }

            finally
            {

                if (myStreamReader != null)
                {
                    myStreamReader.Close();
                }

                Console.WriteLine("Closed the StreamReader.");
            }
        }
        catch
        {

        }
        Console.WriteLine("Done");
        Console.ReadLine();
    }

可以在AppDomain.UnhandledException 事件中注册未处理的异常处理程序。

【讨论】:

  • 太好了,谢谢。那么处理 catch 块中抛出的异常的最佳方法是什么?
  • @user3308043 - 这在很大程度上取决于您是否可以有用做任何事情来让程序继续运行。这是非常具体的情况,没有通用的建议可以提供。鉴于您的代码示例只是一个玩具(而且也是一个损坏的玩具,因为它有错字且无法编译),因此很难针对您的情况提供任何具体建议。如果您无法有效地让程序继续运行,异常终止进程 - 至少不会造成任何进一步的损害。
  • 此视频中的 27:20 标记 youtube.com/…
  • ^^ 如果您查看链接,请确保以 720P 观看视频
  • @user3308043 - 不幸的是,我在工作,我的雇主不是很进步,所以没有适合我的视频。
【解决方案2】:

您的理解不正确。见try-finally

通过使用 finally 块,您可以清理任何资源 在 try 块中分配,即使出现异常也可以运行代码 发生在 try 块中。通常,finally 块的语句 当控制离开 try 语句时运行。控制权转移可以 由于正常执行、执行中断而发生, continue、goto 或 return 语句,或异常传播 在 try 语句之外。

如果你从 try 块中返回,finally 会执行,但如果你从 catch 块中抛出则不会。

但是,如果异常未处理,则执行 finally 块 取决于异常展开操作的触发方式。那, 反过来,这取决于您的计算机的设置方式。

【讨论】:

  • 如果您从catch 块中抛出...只要在其他地方捕获到异常,就会执行它。但是无论如何 +1,你引用了我即将要引用的规范。
  • 27:20 标记在以下视频中:youtube.com/…
  • 他说 finally 块总是在 catch 块中抛出异常的情况下执行
  • 那么他说错了。从 Catch 块抛出的异常是未处理的,除非它是从嵌套的 Try 块中抛出并由嵌套的 Catch 块处理,无穷无尽。
  • 太好了,谢谢。情况似乎如此,我确实开始看到他错了。我也不认为我误解了他,因为他在视频中几乎直接陈述了这一点。
【解决方案3】:

假设没有找到文件,它会先捕获FileNotFoundException:

catch(FileNotFoundException error)
{
    Console.WriteLine(error.Message);
    Console.WriteLine(); 
    throw new Exception();
}

这会向控制台写入一条消息,然后抛出一个新的Exception。但是,此异常未处理并将停止执行。如果您从 Catch 块中抛出异常,则它不会被任何后续块捕获。

解决方案是适当地处理异常而不是抛出一个新异常。如果找不到文件,则对其采取行动,例如让用户选择另一个文件,创建文件等。

【讨论】:

  • 不要扔掉它们,或者在 Catch 块内创建一个嵌套的 Try..Catch 块。这完全取决于您希望用户体验什么。通常,您不想对它们施加异常,而是在代码中解决异常而不让它们注意到。
  • 是的,当然。但是,这似乎最终可能成为多个异常的递归过程。从逻辑上讲,在我看来,您似乎必须有一个默认方法来捕获 catch 块中的异常。
【解决方案4】:

改用throw 试试这个。当你抛出一个新的异常时,实际的异常将会丢失。但是当你只使用throw 时,它会抛出FileNotFoundException 的实际异常。

StreamReader myStreamReader = null;

try
{
   myStreamReader = new StreamReader("c:\\genneric.txt");   
   Console.WriteLine(myStreadReader.ReadToEnd());       
}

catch(FileNotFoundException Error)
{
   Console.WriteLine(Error.Message);
   Console.WriteLine(); 
   throw;
}

catch(Exception Error)
{
   Console.WriteLine(Error.Message);
   Console.WriteLine();
}

finally
{
  Console.WriteLine("Closing the StreamReader.");
  try{
    if(myStreamReader != null)
   {
      myStreamReader.Close();
   }
  } catch(Exception e) {  Console.WriteLine(e.ToString())  };
}


}

【讨论】:

  • 和 throw 有什么区别;并抛出新的异常()?
  • 另外,finally 语句中不需要 try/catch 块,因为在对 myStreamReader 进行 null 测试时不可能出错,因为 myStreamReader 在第一个语句之前设置为 null尝试阻止。如果它从未被创建(创建时出错),它保持为空。
  • @user3308043 我同意,我终于清理了,我只是有一个日志记录,有时甚至是空的,这样如果我有多个清理活动就不会出错。
  • 好的,但在这种情况下确实是多余的。空检查不可能出错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
相关资源
最近更新 更多