День триста пятьдесят девятый. #NetInternals
Начинаю новую серию постов в стиле вопросы и ответы из книги Адама Фурманека «.NET Internals Cookbook».
От автора: "В этой серии я отвечаю на различные вопросы по .NET. Некоторые из них задают на собеседованиях, некоторые я вижу в Интернете, остальные придуманы. Цель - предоставить краткий ответ со ссылками на источники, если это необходимо. Это ни в коем случае не учебник по .NET, это просто набор полезных ответов, чтобы освежить ваши знания."
1. Что происходит, когда вы выбрасываете что-то, что не наследуется от System.Exception? Изменилось ли это со времён .NET 1?
Во-первых, у вас может возникнуть вопрос: «Как вообще это сделать?» Помимо C# существуют другие языки, работающие в CLR. Одним из них является C++/CLI, который является управляемой версией языка C++. В C++ вы можете выбрасывать что угодно –
Выбрасываемый объект помещается в System.Runtime.CompilerServices.RuntimeWrappedException, поэтому вы всё равно сможете перехватить исключение с помощью обычного блока
Однако до .NET 2 всё было иначе. Выброшенный объект оборачивался, поэтому вы не могли поймать его таким образом. Вам пришлось бы использовать нетипизированную версию блока catch в виде
Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
Начинаю новую серию постов в стиле вопросы и ответы из книги Адама Фурманека «.NET Internals Cookbook».
От автора: "В этой серии я отвечаю на различные вопросы по .NET. Некоторые из них задают на собеседованиях, некоторые я вижу в Интернете, остальные придуманы. Цель - предоставить краткий ответ со ссылками на источники, если это необходимо. Это ни в коем случае не учебник по .NET, это просто набор полезных ответов, чтобы освежить ваши знания."
1. Что происходит, когда вы выбрасываете что-то, что не наследуется от System.Exception? Изменилось ли это со времён .NET 1?
Во-первых, у вас может возникнуть вопрос: «Как вообще это сделать?» Помимо C# существуют другие языки, работающие в CLR. Одним из них является C++/CLI, который является управляемой версией языка C++. В C++ вы можете выбрасывать что угодно –
integer, string, byte и т.д. Если вы попытаетесь сделать это в C++/CLI, вы фактически выбросите нечто, что не наследует от System.Exception.Выбрасываемый объект помещается в System.Runtime.CompilerServices.RuntimeWrappedException, поэтому вы всё равно сможете перехватить исключение с помощью обычного блока
catch (Exception e) {}.Однако до .NET 2 всё было иначе. Выброшенный объект оборачивался, поэтому вы не могли поймать его таким образом. Вам пришлось бы использовать нетипизированную версию блока catch в виде
catch {}. По этой причине вы могли видеть код, подобный следующему:tryПосле перехода на .NET 2 этот код перестанет компилироваться, потому что последний блок
{
//
}
catch(Exception e)
{
//
}
catch
{
//
}
catch никогда не будет выполняться. Вы можете изменить код или восстановить старое поведение, используя атрибут RuntimeCompatibilityAttribute.Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
День триста шестьдесят пятый. #NetInternals
2. Как проглотить ThreadAbortException?
Иногда в коде нужно остановить выполнение одного из потоков. Для этого можно использовать метод
Вы можете легко поймать его с помощью блока исключений, но, если вы не сбросите его, оно будет автоматически проброшено выше по стеку.
Еще одна особенность
Также вызов метода
В .NET Core метод
Источники:
- Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
- Евгений Пешков «Особые исключения в .NET» - https://youtu.be/WLSrYgMWif4
2. Как проглотить ThreadAbortException?
Иногда в коде нужно остановить выполнение одного из потоков. Для этого можно использовать метод
thread.Abort(). При вызове метода Abort в останавливаемом потоке выбрасывается исключение ThreadAbortException.Вы можете легко поймать его с помощью блока исключений, но, если вы не сбросите его, оно будет автоматически проброшено выше по стеку.
var thread = new Thread(() => {
try { … }
catch (ThreadAbortException e) {
…
}
});
…
thread.Abort();
Если всё-таки нужно обработать ThreadAbort и выполнить еще какие-то действия в останавливаемом потоке, то можно использовать метод Thread.ResetAbort(). Он прекращает процесс остановки потока и исключение перестаёт прокидываться выше по стеку. Важно понимать, что метод thread.Abort() сам по себе ничего не гарантирует — код в останавливаемом потоке может препятствовать остановке.Еще одна особенность
thread.Abort() заключается в том, что он не сможет прервать код в том случае, если он находится в блоках catch и finally. Внутри кода фреймворка часто можно встретить методы, у которых блок try пустой, а вся логика находится внутри finally. Это делается как раз с той целью, чтобы этот код не мог быть прерван через ThreadAbortException.Также вызов метода
thread.Abort() дожидается выброса ThreadAbortException. Объединим эти два факта и получим, что метод thread.Abort() может заблокировать вызывающий поток:var thread = new Thread(() =>В реальности с этим можно столкнуться при использовании конструкции
{
try { }
catch {
//ThreadAbortException не выбрасывается в catch
}
finally {
//ThreadAbortException не выбрасывается в finally
Thread.Sleep(-1);
}
});
thread.Start();
…
thread.Abort(); //Никогда не вернётся
using. Она разворачивается в try/finally, внутри finally вызывается метод Dispose. Он может быть сколь угодно сложным, содержать вызовы и обработчики событий, использовать блокировки. И если thread.Abort был вызван во время выполнения Dispose, то thread.Abort будет его ждать. Так мы получаем блокировку почти на пустом месте.В .NET Core метод
thread.Abort() выбрасывает PlatformNotSupportedException. И это очень хорошо, потому что мотивирует пользоваться не thread.Abort(), а неинвазивными методами остановки выполнения кода, например с помощью CancellationToken.Источники:
- Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
- Евгений Пешков «Особые исключения в .NET» - https://youtu.be/WLSrYgMWif4
День триста семьдесят первый. #NetInternals
3. Можно ли использовать методы расширения с dynamic?
Нет. Код можно проверить здесь
Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
3. Можно ли использовать методы расширения с dynamic?
Нет. Код можно проверить здесь
using System;Это потому, что методы расширения компилируются в статические. Код выше фактически компилируется в это:
public class Program {
public static void Main() {
int x = 5;
x.Extend();
dynamic y = x;
y.Extend();
}
}
static class Foo {
public static void Extend(this int x) {
Console.WriteLine(x);
}
}
IL_0004: call void Foo::Extend(int32)В C# вы могли бы написать его как:
Foo.Extend(x);Однако DLR (Динамическая Среда Выполнения) ищет методы объекта во время выполнения, поэтому вы не можете использовать расширения с динамическими переменными.
Источник: Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/