Будет ли код в конечном счете срабатывать, если я верну значение в блоке 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 200

12 ответов:

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

нормально, да. Наконец, раздел гарантированно выполняет все, что происходит, включая исключения или оператор 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
    Как из .Объем 2.0, исключение ThreadAbortException не помешает, наконец, от работы. Исключение ThreadAbortException сейчас подняли до или после, наконец. Наконец-то будет всегда запуск и не будет прерван прерыванием потока, если попытка была фактически введена до того, как произошел прерывание потока.

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

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

Как правило, неуправляемый код оборачивается некоторым управляемым классом перед использованием пользовательским кодом. Управляемая оболочка класс обычно использует безопасный дескриптор для обертывания неуправляемого дескриптора. 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 зависит о том, будет ли операционная система инициировать операцию размотки исключения."

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

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

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

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

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

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

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

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

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

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