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