.NET Core: Наконец, блок не вызван необработанным исключением в Linux

Я создал следующую программу на С#:

namespace dispose_test
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var disp = new MyDisposable())
            {
                throw new Exception("Boom");
            }
        }
    }

    public class MyDisposable : IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine("Disposed");
        }
    }
}

Когда я запускаю это с помощью dotnet run, я вижу следующее поведение:

  • Windows: текст исключений, записанный на консоль, "Disposed" напечатан ~ 20 секунд позже, выходы программы.
  • Linux: текст исключений, записанный на консоль, программа немедленно завершает работу. "Disposed" никогда не писал.

Задержка в Windows раздражает, но факт, что Dispose() вообще не вызван на Linux, вызывает беспокойство. Это ожидаемое поведение?

РЕДАКТИРОВКА Разъяснения/дополнения из беседы ниже:

  • Это не относится к using/Dispose(), что является частным случаем try/finally. Поведение также обычно происходит с try/finally - блок finally не запускается. Я обновил название, чтобы отразить это.
  • Я также проверил выполнение Dispose(), записав файл в файловую систему, чтобы убедиться, что проблема не связана с отключением stdout от консоли до запуска Dispose() в случае необработанной исключение. Поведение было одинаковым.
  • Dispose() вызывает вызов, если исключение поймано в любом месте приложения. Это когда он полностью не обрабатывается приложением, что такое поведение происходит.
  • В Windows длительный разрыв не связан с задержкой компиляции. Я начал отсчет времени, когда текст исключения был записан на консоль.
  • Мой оригинальный эксперимент выполнял dotnet run на обеих платформах, что означает отдельные компиляции, но я также попытался сделать dotnet publish в Windows и напрямую запустить вывод на обеих платформах с тем же результатом. Единственное различие заключается в том, что при запуске непосредственно в Linux текст "Aborted (core dumped)" записывается после текста исключения.

Сведения о версии:

  • dotnet --version → 1.0.4.
  • Компиляция в netcoreapp1.1, работающая на .NET Core 1.1.
  • lsb-release -d → Ubuntu 16.04.1 LTS

Ответ 1

Официальный ответ заключается в том, что это ожидаемое поведение.

Интересно, что страница С# doc на try-finally явно вызывает это прямо вверху (выделение)

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

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

Одна вещь, которую я обнаружил в своих экспериментах, состоит в том, что, похоже, ее недостаточно, чтобы поймать исключение, вам тоже нужно ее обработать. Если выполнение выходит из блока catch через throw, то, наконец, не будет выполняться.

Ответ 2

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

    static void Main(string[] args)
    {
        try
        {
            using (var disp = new MyDisposable())
            {
                throw new Exception("Boom");
            }
        }
        catch (Exception e)
        {
            throw;
        }
    }