Будет ли код в операторе Finally срабатывать, если я верну значение в блоке Try?

Я просматриваю некоторый код для друга и говорю, что он использовал оператор return внутри блока try-finally. Код в разделе Finally все еще срабатывает, хотя остальная часть блока try этого не делает?

пример:

public bool someMethod()
{
  try
  {
    return true;
    throw new Exception("test"); // doesn't seem to get executed
  }
  finally
  {
    //code in question
  }
}

12 ответов


простой ответ: да.


нормально, да. Раздел finally гарантированно выполняет все, что происходит, включая исключения или оператор return. Исключением из этого правила является асинхронное исключение в потоке (OutOfMemoryException, StackOverflowException).

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


вот небольшой тест:

class Class1
{
    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine("before");
        Console.WriteLine(test());
        Console.WriteLine("after");
    }

    static string test()
    {
        try
        {
            return "return";
        }
        finally
        {
            Console.WriteLine("finally");
        }
    }
}

результат:

before
finally
return
after

цитата из MSDN

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


вообще да, наконец-то будет работать.

для следующих трех сценариев, наконец, будет всегда run:

  1. никаких исключений
  2. синхронные исключения (исключения, возникающие в обычном потоке программы).
    Это включает CLS-совместимые исключения, производные от System.Исключения и исключения, не совместимые с CLS, которые не являются производными от System.Исключение. CLS-несовместимые исключения автоматически оборачиваются RuntimeWrappedException. C# не может создавать исключения, отличные от CLS, но такие языки, как C++, могут. C# может вызывать код, написанный на языке, который может создавать исключения, не совместимые с CLS.
  3. Асинхронное ThreadAbortException
    Начиная с .NET 2.0 исключение ThreadAbortException больше не будет препятствовать запуску finally. ThreadAbortException теперь поднимается до или после finally. Наконец-то будет всегда выполнить и не будет прерываться прерыванием потока, если попытка была фактически введена до прерывания потока.

следующий сценарий, наконец, не будет запущен:

Асинхронное Исключение StackOverflowException.
Начиная с .NET 2.0 переполнение стека приведет к завершению процесса. Finally не будет выполняться, если только не будет применено дополнительное ограничение, чтобы сделать finally CER (область ограниченного выполнения). ССВ не должно быть в пользовательском коде. Они должны использоваться только там, где важно, чтобы код очистки всегда выполнялся-после того, как процесс завершает работу при переполнении стека, и поэтому все управляемые объекты будут очищены по умолчанию. Таким образом, единственное место, где ССВ должен быть релевантным, - это ресурсы, выделяемые вне процесса, например неуправляемые дескрипторы.

Как правило, неуправляемый код оборачивается некоторым управляемым классом перед использованием кода пользователя. Управляемая оболочка класс обычно использует SafeHandle для обертывания неуправляемого дескриптора. SafeHandle реализует критический финализатор и метод выпуска, который выполняется в CER, чтобы гарантировать выполнение кода очистки. По этой причине, вы не должны видеть ССВ валялись в пользовательском коде.

поэтому тот факт, что finally не запускается на StackOverflowException, не должен влиять на пользовательский код, так как процесс все равно завершится. Если у вас есть крайний случай, где вам нужно очистка некоторого неуправляемого ресурса за пределами SafeHandle или CriticalFinalizerObject, затем используйте CER следующим образом; но обратите внимание, что это плохая практика-неуправляемая концепция должна быть абстрагирована в управляемый класс(Ы) и соответствующий SafeHandle(ы) по дизайну.

например,

// No code can appear after this line, before the try
RuntimeHelpers.PrepareConstrainedRegions();
try
{ 
    // This is *NOT* a CER
}
finally
{
    // This is a CER; guaranteed to run, if the try was entered, 
    // even if a StackOverflowException occurs.
}

есть очень важное исключение из этого, которое я не видел в каких-либо других ответах, и которое (после программирования на C# в течение 18 лет) я не могу поверить, что не знал.

если вы бросаете или инициировать исключение любой сортировка внутри catch блок (не просто странно StackOverflowExceptions и вещи такого рода), и у вас нет всего try/catch/finally блок внутри другого try/catch заблокировать ваш finally блок не будет выполняться. Это легко продемонстрировать - и если я сам не видел, учитывая, как часто я читал, что это только действительно странные, крошечные угловые случаи, которые могут вызвать finally блок не выполнить, я бы не поверил.

static void Main(string[] args)
{
    Console.WriteLine("Beginning demo of how finally clause doesn't get executed");
    try
    {
        Console.WriteLine("Inside try but before exception.");
        throw new Exception("Exception #1");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception).");
        throw;
    }
    finally
    {
        Console.WriteLine("This never gets executed, and that seems very, very wrong.");
    }

    Console.WriteLine("This never gets executed, but I wasn't expecting it to."); 
    Console.ReadLine();
}

Я уверен, что для этого есть причина, но странно, что это не более широко известно. (Это отмечено здесь например,но не в этом конкретном вопросе.)


Я понимаю, что опаздываю на вечеринку, но в сценарии (отличающемся от примера OP), где действительно возникает исключение состояний MSDN (https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx): " Если исключение не поймано, выполнение блока finally зависит о том, выбирает ли операционная система для запуска операции размотки исключения."

блок finally-это только гарантированный выполнить, если какая-то другая функция (например, Main) далее стек вызовов ловит исключение. Эта деталь обычно не является проблемой, потому что все среды выполнения (CLR и OS) программы c# работают на свободном большинстве ресурсов, принадлежащих процессу, когда он выходит (файл обрабатывает и т. д.). В некоторых случаях это может быть важно: операция базы данных наполовину выполняется, которую вы хотите совершить resp. размотайте; или какое-то удаленное соединение, которое не может быть автоматически закрыто ОС, а затем блокирует сервер.


да. В этом, по сути, и состоит главный пункт окончательного утверждения. Если происходит что-то catestrophic (из памяти, компьютер отключен, и т. д.) оператор finally должен всегда выполняться.


наконец-то не будет работать, в случае если вы выходите из приложения, используя Система.выход(0); как в

try
{
    System.out.println("try");
    System.exit(0);
}
finally
{
   System.out.println("finally");
}

результат будет просто : попробуй!--2-->


Он также не будет срабатывать при необработанном исключении и работает в потоке, размещенном в службе Windows

наконец не выполняется, когда в потоке, работающем в службе Windows


99% сценариев будет гарантировано, что код внутри finally блок будет работать, однако, подумайте об этом сценарии: у вас есть поток, который имеет try ->finally заблокировать (не catch), и вы получите необработанное исключение в этом потоке. В этом случае поток выйдет и его finally блок не будет выполнен (приложение может продолжать работать в данном случае)

этот сценарий довольно редок, но это только для того, чтобы показать, что ответ не всегда " да", в большинстве случаев это" да", а иногда, в редких случаях,"нет".


основная цель finally block-выполнить все, что написано внутри него. Она не должна зависеть от того, что происходит в try или catch.Однако с системой.Окружающая среда.Exit (1) приложение выйдет, не переходя к следующей строке кода.