Будет ли код в объявлении finally загораться, если я верну его значение в блоке Try?

Я просматриваю код для друга и говорю, что он использовал оператор return внутри блока try-finally. Сохраняет ли код в разделе "Окончание", даже если остальная часть блока try не работает?

Пример:

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

Ответ 1

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

Ответ 2

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

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

Ответ 3

Здесь немного теста:

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

Ответ 4

Цитата из MSDN

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

Ответ 5

Как правило, да, наконец, будет работать.

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

  • Исключения отсутствуют
  • Синхронные исключения (исключения, которые происходят в обычном потоке программы).
    Это включает исключения, совместимые с CLS, которые вытекают из исключений System.Exception и не связанных CLS, которые не являются результатом System.Exception. Исключения, не связанные с CLS, автоматически обертываются RuntimeWrappedException. С# не может вызывать исключения для исключения CLS, но такие языки, как С++, могут. С# может вызывать код, написанный на языке, который может вызывать исключения, не совместимые с CLS.
  • Асинхронное исключение ThreadAbortException
    Начиная с .NET 2.0 исключение ThreadAbortException больше не будет препятствовать окончанию работы. ThreadAbortException теперь поднимается до или после окончательного. Окончание всегда будет выполняться и не прерывается прерыванием потока, если попытка была фактически введена до того, как произошел прерывание потока.

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

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

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

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

например.

// 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.
}

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

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

результат будет справедливым: попробуйте

Ответ 11

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

Этот сценарий довольно редок, но только для того, чтобы показать, что ответ не ВСЕГДА "Да", он большую часть времени "Да", а иногда и в редких условиях "Нет".

Ответ 12

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