Подтвердить что ты не робот

С# Время окончательного исполнения

Возьмите этот код:

using System;

namespace OddThrow
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                throw new Exception("Exception!");
            }
            finally
            {
                System.Threading.Thread.Sleep(2500);
                Console.Error.WriteLine("I'm dying!");
                System.Threading.Thread.Sleep(2500);
            }
        }
    }
}

Что дает мне этот результат:

Unhandled Exception: System.Exception: Exception!
   at OddThrow.Program.Main(String[] args) in C:\Documents and Settings\username
\My Documents\Visual Studio 2008\Projects\OddThrow\OddThrow\Program.cs:line 14
I'm dying!

Мой вопрос: почему текст необработанного исключения возникает до окончательного? На мой взгляд, наконец, следует исключить, поскольку пакет распаковывается, прежде чем мы даже знаем, что это исключение необработано. Обратите внимание на вызовы Sleep() - они возникают после печати необработанного исключения, как если бы оно выполнялось следующим образом:

  • Необработанный текст/сообщение исключений
  • Наконец блоки.
  • Завершить приложение

В соответствии со стандартом С#, §8.9.5, это неправильное поведение:

  • В текущем члене функции рассматривается каждый пример try, который охватывает точку бросания. Для каждого оператора S, начиная с самого внутреннего оператора try и заканчивая внешней инструкцией try, выполняются следующие шаги:
    • Если блок try из S заключает точку бросания и если S имеет один или несколько кластеров catch, предложения catch рассматриваются в порядке появления, чтобы найти подходящий обработчик для исключения. Первое предложение catch, которое указывает тип исключения или базовый тип типа исключения, считается совпадением. Общее предложение catch (§8.10) считается совпадением для любого типа исключения. Если расположено соответствующее предложение catch, распространение исключения завершается передачей элемента управления блоку этого предложения catch.
    • В противном случае, если блок try или блок catch из S заключает точку бросания, и если S имеет окончательный блок, управление передается блоку finally. Если блок finally выдает другое исключение, обработка текущего исключения прекращается. В противном случае, когда управление достигает конечной точки блока finally, обработка текущего исключения продолжается.
  • Если обработчик исключений не был найден в текущем вызове члена функции, вызов члена функции прекращается. Затем эти шаги повторяются для вызывающего элемента функции с точкой броска, соответствующей оператору, из которого был вызван элемент функции.
  • Если обработка исключений завершает все вызовы членов функции в текущем потоке, указывая, что поток не имеет обработчика исключения, то сам поток завершается. Влияние такого завершения определяется реализацией.

Где я ошибаюсь? (У меня есть отдельные сообщения об ошибках консоли, и это в свою очередь. Мало, просто раздражает и заставляет меня подвергать сомнению язык...)

4b9b3361

Ответ 1

Стандартные утверждения о порядке выполнения правильны и не противоречат тому, что вы наблюдаете. Сообщение "Необработанное исключение" разрешено появляться в любой момент процесса, потому что это просто сообщение из CLR, а не собственно обработчик исключений. Правила порядка исполнения применяются только к выполняемому в CLR коде, а не к тому, что делает CLR.

То, что вы на самом деле сделали, - это раскрытие детали реализации, которая заключается в том, что необработанные исключения распознаются, глядя на стек, в котором try {} блокирует нас внутри, а не фактически исследует весь путь к корню. Исключения могут обрабатываться или не обрабатываться, рассматривая этот стек, но необработанные исключения распознаются таким образом.

Как вы, возможно, знаете, если вы поместите функцию {} catch {} на уровне верхнего уровня в свою основную функцию, то вы увидите поведение, которое вы ожидаете: каждая функция, наконец, будет выполнена, прежде чем проверять следующий фрейм для сопоставление catch {}.

Ответ 2

Я мог бы быть в стороне от того, как я читаю вещи... но у вас есть попытка окончательно блокировать без улова.

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

Ответ 3

Вывод выполняется из обработчика исключений CLR по умолчанию. Обработчики исключений происходят до блока finally. После окончательного блока CLR завершается из-за необработанного исключения (он не может завершиться раньше, поскольку С# гарантирует [1], что вызывается предложение finally).

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

[1] гарантируется во время нормальной работы, по крайней мере, при отсутствии внутренних ошибок времени выполнения или отключения питания.

Ответ 5

Чтобы добавить больше в микс, рассмотрите следующее:

using System;
namespace OddThrow
{
    class Program
    {
        static void Main()
        {
            AppDomain.CurrentDomain.UnhandledException +=
                delegate(object sender, UnhandledExceptionEventArgs e)
            {
                Console.Out.WriteLine("In AppDomain.UnhandledException");
            };
            try
            {
                throw new Exception("Exception!");
            }
            catch
            {
                Console.Error.WriteLine("In catch");
                throw;
            }
            finally
            {
                Console.Error.WriteLine("In finally");
            }
        }
    }
}

Что на моей системе (норвежский) показывает это:

[C:\..] ConsoleApplication5.exe
In catch
In AppDomain.UnhandledException

Ubehandlet unntak: System.Exception: Exception!
   ved OddThrow.Program.Main() i ..\Program.cs:linje 24
In finally

Ответ 6

Хотя это и не совсем ожидалось, программа ведет себя так, как должна. Ожидается, что первый блок не будет запущен, ожидается, что он будет всегда запускаться.

Я скорректировал ваш образец:

public static void Main()
{
    try
    {
        Console.WriteLine("Before throwing");
        throw new Exception("Exception!");
    }
    finally
    {
        Console.WriteLine("In finally");
        Console.ReadLine();
    }
}

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

Ответ 7

Блоки try-catch-finally работают точно так, как вы ожидали, если они пойманы в какой-то момент. Когда я написал тестовую программу для этого, и я использую различные уровни вложенности, единственный случай, когда он вел себя таким образом, который соответствовал тому, что вы описали, когда исключение было полностью необработанным кодом, и оно появилось в операционной системе.

Каждый раз, когда я запускал его, ОС создавала сообщение об ошибке. Таким образом, проблема связана не с С#, а с тем, что ошибка, которая не обрабатывается пользовательским кодом, больше не находится под контролем приложения, и поэтому среда выполнения (я считаю) не может заставить шаблон выполнения на нем.

Если вы создали приложение для формы Windows и написали все свои сообщения в текстовое поле (а затем сразу же промоило их) вместо того, чтобы напрямую писать на консоль, вы бы вообще не увидели это сообщение об ошибке, поскольку оно было вставлено в консоль ошибок вызывающим приложением, а не вашим собственным кодом.

EDIT

Я попытаюсь выделить ключевую часть этого. Необработанные исключения выходят из-под вашего контроля, и вы не можете определить, когда их обработчик исключений будет выполнен. Если вы поймаете исключение в какой-то момент вашего приложения, то блоки finally будут выполняться перед блоком ниже catch.

Ответ 8

Попробуйте/окончательно без catch будет использовать обработчик по умолчанию, который делает именно то, что вы видите. Я использую его все время, например, в тех случаях, когда обработка исключения будет охватывать ошибку, но есть еще какая-то очистка, которую вы хотите сделать.

Также помните, что вывод в стандартную ошибку и стандартную версию буферизуется.

Ответ 9

Чтобы собрать пару ответов вместе, произойдет следующее: если у вас есть Unhandled Exception, UnhandledExceptionEvent будет добавлен в AppDomain, тогда код продолжит выполнение (т.е. наконец). Это Статья MSDN на мероприятии

Ответ 10

Следующая попытка:

  • Я считаю, что этот случай не упоминается в стандарте С#, и я согласен, что это почти противоречит ему.

  • Я считаю, что внутренняя причина, по которой это происходит, выглядит примерно так: CLR регистрирует обработчик исключений по умолчанию в качестве обработчика SEH в FS: [0] когда у вас больше уловов в вашем коде, эти обработчики добавляются в цепочку SEH. В качестве альтернативы, во время обработки SEH вызывается только обработчик CLR и обрабатывает внутреннюю цепочку исключений CLR, я не знаю, какой из них.

В вашем коде, когда генерируется исключение, в цепочке SEH используется только обработчик по умолчанию. Этот обработчик вызывается до начала разворачивания стека.

Обработчик исключений по умолчанию знает, что в стеке нет обработчика исключений. Поэтому он сначала вызывает весь зарегистрированный обработчик UnhandledException, затем выводит свое сообщение об ошибке и маркирует AppDomain для разгрузки.

Только после того, как разворачивается стека, и, наконец, блоки называются в соответствии со стандартом С#.

Как я вижу, способ, которым CLR обрабатывает необработанное исключение, не рассматривается в стандарте С#, а только порядок, в котором finallys вызывается во время разворачивания стека. Этот порядок сохраняется. После этого "Влияние такого прекращения определяется реализацией". вступает в силу.