catch 块中的异常意味着 finally 块永远不会执行?

Exception in catch block means the finally block never executes?

我在 C# 中有一个简单的 try-catch-finally 块。据我了解,"finally" 块很有用,因为即使在 catch 块内抛出异常(除非有一些特殊的异常类型),它的代码也会执行。

但是,在下面的简单示例中,finally 块永远不会执行。 Visual Studio 说我的 catch 块中发生了未处理的异常,然后程序终止。我以为执行只会跳转到 finally 块。

如何保证finally块中的代码在catch块发生异常时执行?

public static void Main(string[] args)
{
    try
    {
        throw new Exception("Apple");
    }

    catch (Exception ex)
    {
        throw new Exception("Banana");
    }

    finally
    {
        // This line never executes. Why?
        Console.WriteLine("Carrot");
    }
}

你抛出了第二个异常,没有捕获,第二个异常将不会继续代码..

Unhandled Exception: System.Exception: Banana

我建议你处理你的第一个异常,而不是抛出另一个。

static void Main(string[] args)
{
   var x = 2;
   try
   {
       if(x >1) throw new Exception("Apple");
   }
   catch (Exception ex)
   {
       x = 1;
   }
   finally
   {
      Console.WriteLine("Carrot");
   }
}

如果您想了解更多关于异常处理的信息,RTFM:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/exceptions/exception-handling

发生了什么以及为什么会发生

结果取决于您在程序崩溃时单击了哪个按钮。如果你的速度很慢,Windows 错误报告 (WER) 对话框将显示 "Debug" 和 "Close program"。如果您按下 "Close program" 按钮,程序将被操作系统终止,没有任何机会向控制台写入其他内容。

如果您足够快地点击 "Cancel" 按钮,那么 Windows 错误报告部分将被取消并且控制权返回到您的程序。然后它将 "Carrot" 写入控制台。

因此,这不是 .NET 问题,而是 Windows 如何对 exception dispatching 作出反应的问题。

如何控制它

要禁用 WER 对话框,您可以使用 WerAddExcludedApplication . To get rid of the Debug dialog, you can use SetErrorMode

请熟悉使用这些方法的缺点。阅读Raymond Chen's comments on WerAddExcludedApplication and check whether SetThreadErrorMode可能会有所帮助。

您的代码可能如下所示:

using System;
using System.Runtime.InteropServices;

namespace ExceptionInCatch
{
    class Program
    {
        [DllImport("wer.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern int WerAddExcludedApplication(String pwzExeName, bool bAllUsers);

        [Flags]
        public enum ErrorModes : uint
        {
            SYSTEM_DEFAULT = 0x0,
            SEM_FAILCRITICALERRORS = 0x0001,
            SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
            SEM_NOGPFAULTERRORBOX = 0x0002,
            SEM_NOOPENFILEERRORBOX = 0x8000,
            SEM_NONE = SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX
        }

        [DllImport("kernel32.dll")]
        static extern ErrorModes SetErrorMode(ErrorModes uMode);


        public static void Main(string[] args)
        {
            var executableName = AppDomain.CurrentDomain.FriendlyName;
            WerAddExcludedApplication(executableName, false);
            SetErrorMode(ErrorModes.SEM_NONE);
            try
            {
                throw new Exception("Apple");
            }
            catch (Exception ex)
            {
                throw new Exception("Banana");
            }
            finally
            {
                // This line will now execute
                Console.WriteLine("Carrot");
            }
        }
    }
}

以下是正确引发和处理异常的一些规则。我发现它们对我自己非常有用,并且喜欢 link 它们,因为当出现此类问题时,它们是一种资源。我在您的示例代码和您的问题中看到了许多经典错误:

http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET

Finally 块总是无误地执行。除非通过任务管理器终止整个进程,否则无法跳过它们。他们 运行 在 return 之后,在 catch 块中的另一个异常之后,在一切之后。异常处理只是一个代码流,主要由编译器强制执行。