5

由于某种原因,在我的控制台应用程序中,我无法让 finally 块运行。我正在编写这段代码来测试 finally 块是如何工作的,所以它非常简单:

static void Main()
{
    int i = 0;
    try
    {
        int j = 1 / i; // Generate a divide by 0 exception.
    }
    finally
    {

        Console.Out.WriteLine("Finished");
        Console.In.ReadLine();
    }
}

起初我遇到了这里描述的问题,但后来我尝试在 Visual Studio 之外运行程序,但出现“程序已停止响应”错误。

4

5 回答 5

13

因为您没有顶级异常处理程序,.Net 运行时会为您捕获异常并在 finally 有机会运行之前中止程序。这说明了这一点:

static void Main() 
{
  try
  {
      int i = 0;
      try
      {
         int j = 1 / i; // Generate a divide by 0 exception.
      }
      finally
      {
          Console.Out.WriteLine("Finished");
          Console.In.ReadLine();     
      }
  }
  catch (Exception ex)
  {
      Console.WriteLine(ex.ToString());
  }
}

使用此代码,异常现在在调用链中通过 try...catch 处理(它恰好在同一个方法中),因此嵌入的 finally 将被执行。catch 不必在引发异常的同一个函数中,它可以在调用链中的任何位置。

编辑:最初,您的程序将在何时何地捕获异常似乎不确定。但是想想你的应用程序的边界,外部世界与你的代码交互的地方——通常它们是有限的并且定义明确的。因此对于控制台应用程序,边界是 Main 方法,这是您可以放置​​顶级异常处理程序的地方。对于 Web 表单应用程序,边界包括按钮单击事件(用户正在与您的 UI 交互)等事件,因此您也可以在其中包含异常处理程序。对于类库,边界通常是调用库的应用程序的边界,而不是库本身。所以不要在库中捕获异常(除非您可以明智地从中恢复),而是让它们冒泡到调用应用程序。

于 2012-08-28T16:09:56.757 回答
3

想在这里添加我自己的发现,因为这里的行为肯定很奇怪——因此,接受的答案并不完全正确。

给定以下示例:

static void Main(string[] args)
{
    try{
        throw new Exception("");
    } finally {
        Console.WriteLine("I am never called!");
        Console.ReadLine();
    }
}

如果我们选择 CANCEL 退出 Windows 错误报告对话框,则实际执行 finally 块,如下所示:

Windows 错误报告正在进行中 完成后的控制台

但是,如果我们允许 Windows 错误报告对话框“完成”,因此我们可以选择“调试”或“关闭程序”,那么 finally 块不会被执行。

在此处输入图像描述 在此处输入图像描述


对我来说,这表明 .NET 运行时将实际运行所有 finally 块,无论遇到“未处理的顶级异常”,并且阻止它这样做的实际上是 Windows(如果您选择“关闭程序”)或 Visual Studio 调试器(如果您选择“调试”或从附加的调试器开始)......因为他们在运行时之前终止进程作为继续的机会。

有什么想法吗?

于 2019-08-28T10:03:18.483 回答
1

在一个更大的程序中,这不会是一个问题,因为DevideByZero异常会“冒泡”并希望在其他地方处理。因为这是在main方法中,所以异常无处可去。这会导致您看到的问题...

因此,以下内容会按您的预期进行

static void Main(string[] args)
{
    try
    {
        CatchTest();
    }
    catch (Exception)
    {

    }
}

private static void CatchTest()
{
    int i = 0;
    try
    {
        int j = 1 / i; // Generate a divide by 0 exception.    
    }
    finally
    {
        Console.Out.WriteLine("Finished");
        Console.In.ReadLine();
    }
}

我希望这有帮助。

于 2012-08-28T16:07:43.977 回答
0

您仍然需要一个 catch 来捕获抛出的异常:

int i = 0;
        try
        {
            int j = 1 / i; // Generate a divide by 0 exception.
        }
        catch(Exception e)
        {
            Console.Out.WriteLine("Exception caught");
        }
        finally
        {

            Console.Out.WriteLine("Finished");
            Console.In.ReadLine();
        }

此异常导致应用程序崩溃,并且该异常未处理因此终止应用程序。

http://msdn.microsoft.com/en-us/library/zwc8s4fz(v=vs.100).aspx

通常,当未处理的异常结束应用程序时,finally 块是否运行并不重要。但是,如果即使在这种情况下也必须在 finally 块中运行语句,则一种解决方案是在 try-finally 语句中添加一个 catch 块。

于 2012-08-28T16:01:27.763 回答
0

您需要分离调试器(例如,在发布模式下运行您的应用程序),有关更多详细信息,请参阅以下(相关)问题:finally doesn't seem to perform in C# console application while using F5

于 2012-08-28T16:10:21.333 回答