Исключение в блоке catch означает, что блок finally никогда не выполняется?

У меня есть простой блок 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");
    }
}

Ответ 1

Что и почему это происходит

Результат зависит от того, какую кнопку вы нажимаете при сбое программы. Если вы не уверены, в диалоговом окне "Отчет об ошибках Windows" отобразятся "Отладка" и "Закрыть программу". Если вы нажмете кнопку "Закрыть программу", программа будет остановлена ​​системой operationg без каких-либо шансов написать что-то еще для консоли.

Снимок экрана: закрыть программу

Если вы достаточно быстро нажмете кнопку "Отмена", то часть отчета об ошибках Windows будет отменена, и управление вернется к вашей программе. Затем он напишет "Морковь" на консоль.

Снимок экрана: отменить

Следовательно, это не проблема .NET, но вопрос о том, как Windows реагирует на диспетчеризацию исключений.

Как получить контроль над ним

Чтобы отключить диалог WER, вы можете использовать WerAddExcludedApplication. Чтобы избавиться от диалога Debug, вы можете использовать SetErrorMode.

Пожалуйста, ознакомьтесь с недостатками использования этих методов. Прочитайте Раймонд Чэнь комментирует WerAddExcludedApplication и проверяет может ли 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");
            }
        }
    }
}

Ответ 2

вы бросаете второе исключение, без catch, второе исключение не будет продолжать код.

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

Ответ 3

Вот некоторые правила для правильного создания и обработки исключений. Я нашел для себя очень полезным, и хотел бы связать их, как с ресурсом, когда возникает вопрос, подобный этому. Я вижу ряд классических ошибок в вашем примере кода и действительно в вашем самом вопросе:

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

Наконец, блоки всегда выполняются без сбоев. Не дожидаясь полного уничтожения всего процесса с помощью диспетчера задач, нет возможности пропустить их. Они запускаются после возвращения, после другого исключения в блоке catch, после всего. Обработка исключений - это всего лишь поток кода, который в значительной степени выполняется компилятором.