finally
выполняется, когда элемент управления покидает блок try
, к которому он присоединен по любой причине ; не только если бы был блок catch
на том же уровне - это был бы обработчик fault
(в терминах CLR), который, я думаю, мы все еще не можем сделать в C #.
Таким образом, он входит в finally
, который печатает A
, потому что контроль покидает этот блок try
. Он покидает его из-за исключения, которое попадает во внешний блок catch
. Однако это не меняет времени / порядка.
Обратите внимание, что есть некоторые странности, которые возможны, если исключение полностью не обнаружено в вашем коде. Обработка исключений происходит в два этапа. На первом этапе он пытается найти соответствующее предложение catch
для исключения, которое может включать выполнение защитных предложений (when
, C # 6 и более поздние версии). На втором этапе он разматывает стек и выполняет все предложения finally
в соответствии с требованиями вложенности, прежде чем достигнет правильного уровня, на котором определено условие catch
, которое будет обрабатывать исключение 1 .
Я считаю, что в некоторых средах, если на первом этапе не удается найти соответствующий обработчик исключений (предложение catch
) на всех , вся управляемая среда может быть разрушена. При таких обстоятельствах пункты finally
не выполняются.
Все, что требуется для стандарта, соответствующего CLR, описано в документе MS Partition I.pdf, который можно найти в ECMA C # и стандартах инфраструктуры общего языка . Раздел 12.4.2.5 гласит:
Когда возникает исключение, CLI
ищет в массиве первый защищенный блок, который
Защищает область, включая указатель текущей инструкции и
Является ли блок обработчика захвата и
Чей фильтр хочет обработать исключение
Если совпадение не найдено в текущем методе, выполняется поиск вызывающего метода и т. Д. Если совпадений не найдено
CLI выведет трассировку стека и прервет программу.
(Мой Акцент )
1 Иллюстрировано с использованием варианта Пример Flydog57 , а также локальные функции C # 7:
using System;
namespace PlayAreaCSCon
{
internal class Program
{
static void Main(string[] args)
{
TestTryCatchFinally();
Console.WriteLine("Complete");
Console.ReadLine();
}
private static void TestTryCatchFinally()
{
try
{
Console.WriteLine("Start Outer Try");
try
{
Console.WriteLine("Start Inner Try");
throw new Exception("Exception from inner try");
}
finally
{
Console.WriteLine("In Inner Finally");
}
}
catch (Exception ex) when (GuardHelper(ex))
{
Console.WriteLine("In outer catch");
}
finally
{
Console.WriteLine("In outer finally");
}
bool GuardHelper(Exception ex)
{
Console.WriteLine("In outer guard");
return true;
}
}
}
}
Это печатает:
Start Outer Try
Start Inner Try
In outer guard
In Inner Finally
In outer catch
In outer finally
Complete
Иллюстрирование двухпроходного характера обработки исключений (при этом In outer guard
печатается до In Inner Finally
)