Ответ 1
Простой ответ: Да.
Я просматриваю код для друга и говорю, что он использовал оператор return внутри блока try-finally. Сохраняет ли код в разделе "Окончание", даже если остальная часть блока try не работает?
Пример:
public bool someMethod()
{
try
{
return true;
throw new Exception("test"); // doesn't seem to get executed
}
finally
{
//code in question
}
}
Простой ответ: Да.
Обычно да. Окончательный раздел гарантированно выполняет все, что происходит, включая исключения или оператор возврата. Исключением из этого правила является асинхронное исключение, происходящее в потоке (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
finally используется для гарантии выполнения блока кода кода независимо от того, как предыдущий try блок вышел.
Как правило, да, наконец, будет работать.
В следующих трех сценариях, наконец, будет ВСЕГДА работать:
Следующий сценарий, наконец, не будет выполняться:
Асинхронное исключение 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.
}
Я понимаю, что опаздываю на вечеринку, но в сценарии (отличаясь от примера OP), где действительно исключение выражается в состояниях MSDN (https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx): "Если исключение не поймано, выполнение блока finally зависит от от того, хочет ли операционная система инициировать операцию отмены исключения".
Окончательный блок гарантированно будет выполняться только в том случае, если какая-либо другая функция (такая как Main) дальше, столбец вызовов улавливает исключение. Эта деталь обычно не является проблемой, потому что все программы С# для среды выполнения (CLR и OS) запускаются на свободных ресурсах, которые принадлежит процессу при выходе (файлы и т.д.). В некоторых случаях это может иметь решающее значение: операция с базой данных наполовину, которую вы хотите совершить, или. размотать; или какое-либо удаленное соединение, которое может не быть автоматически закрыто ОС, а затем блокирует сервер.
Там очень важное исключение, которое я не видел в других ответах, и которое (после программирования на С# в течение 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();
}
Я уверен, что есть причина для этого, но это странно, что он не более широко известен. (Здесь он отметил здесь, но не в этом конкретном вопросе.)
Да. На самом деле это основной пункт окончательного утверждения. Если не произойдет что-то сопутствующее (вне памяти, компьютер отключен и т.д.), Оператор finally всегда должен выполняться.
наконец, не будет работать в случае, если вы выходите из приложения, используя System.exit(0); как в
try
{
System.out.println("try");
System.exit(0);
}
finally
{
System.out.println("finally");
}
результат будет справедливым: попробуйте
Он также не будет запускать неперехваченное исключение и работать в потоке, размещенном в службе Windows
Наконец, не выполняется, когда в потоке, запущенном в службе Windows
На 99% сценариев будет гарантировано, что код внутри блока finally
будет запущен, однако подумайте об этом сценарии: у вас есть поток с блоком try
→ finally
(no catch
), и вы получите необработанное исключение в этом потоке. В этом случае поток выйдет, и его блок finally
не будет выполнен (приложение может продолжать работать в этом случае)
Этот сценарий довольно редок, но только для того, чтобы показать, что ответ не ВСЕГДА "Да", он большую часть времени "Да", а иногда и в редких условиях "Нет".
Основная цель блока finally - выполнить все, что написано внутри него. Он не должен зависеть от того, что происходит при попытке или catch.However с System.Environment.Exit(1) приложение выйдет без перехода к следующей строке кода.