Сработает ли код в операторе finally, если я верну значение в блоке Try? - PullRequest
218 голосов
/ 05 декабря 2008

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

Пример:

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

Ответы [ 12 ]

245 голосов
/ 05 декабря 2008

Простой ответ: Да.

196 голосов
/ 05 декабря 2008

Обычно да. Секция finally гарантированно выполнит все, что происходит, включая исключения или оператор возврата. Исключением из этого правила является асинхронное исключение, происходящее в потоке (OutOfMemoryException, StackOverflowException).

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

144 голосов
/ 06 декабря 2008

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

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
36 голосов
/ 06 декабря 2008

Цитирование из MSDN

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

18 голосов
/ 23 июня 2011

Вообще-то да, наконец-то побежит.

Для следующих трех сценариев, наконец, будет ВСЕГДА работать:

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

В следующем сценарии, наконец, не будет работать:

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

Как правило, неуправляемый код переносится каким-либо управляемым классом перед использованием пользовательским кодом. Класс управляемой оболочки обычно использует SafeHandle для переноса неуправляемого дескриптора. SafeHandle реализует критический финализатор и метод Release, который запускается в CER, чтобы гарантировать выполнение кода очистки. По этой причине вы не должны видеть, что 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.
}
9 голосов
/ 29 января 2018

Есть очень важное исключение из этого, о котором я не упоминал ни в каких других ответах, и которое (после программирования на 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();
}

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

7 голосов
/ 26 февраля 2015

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

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

3 голосов
/ 05 декабря 2008

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

2 голосов
/ 01 июля 2015

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

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

2 голосов
/ 03 июля 2014

, наконец, не запустится, если вы выходите из приложения, используя System.exit (0); как в

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

результат будет просто: попробовать

...