Заметки про C#. А знали ли вы?
Запись от netBool размещена 08.05.2018 в 12:29
Погружаясь в леса и дебри Reflection и IL, постепенно узнаю много новых вещей о .NET, о которых раньше даже не задумывался. И хотел бы поделиться некоторыми из них (буду дописывать по мере наличия свободного времени): 1. Тип void Знали ли вы, что в C# void - не просто ключевое слово для обозначения функции, не возвращающей значение, а полноценных тип данных? Да, да. Несколько фактов о нем:
2. Делегаты На уровне IL все - классы, интерфейсы, структуры и даже делегаты - все является классами. Но делегаты выделяются из всей этой кучи. Delegate - это особый класс в .NET Framework. Он не объявлен как sealed, но мы не можем явно наследовать от него свои классы (так же, как и от MulticastDelegate). Но когда мы пишем
3. Исходники Майкрософт выложила исходный код .NET Framework, а так же компилятора Roslyn, изъяв ото всюду старый нативный компилятор. И у многих программистов сложилось впечатление, что весь .NET написан на C#. Но мало кто заглядывал под капот: сам jit-компилятор, "виртуальная машина" и многое другое написаны на С++ и assembler, в том числе assembler ARM/ARMx64 4. Хранение больших объектов в памяти Принято считать, что CLR для ссылочных типов хранит только ссылку на объект, а сам под сам объект выделяется память где-то в куче. Но мало кто догадывается, поскольку об этом не пишут в учебниках, что Цитата:
CLR работает с объектами, размер которых больше или равен 85000 байтам, иначе, чем с объектами меньшего размера. Большие объекты создаются в Large Object Heap (LOH), а объекты меньшего размера — в обычной куче, GC Heap, что позволяет оптимизировать выделение памяти под объекты и сбор мусора. LOH не уплотняется, а GC Heap уплотняется при каждом сборе мусора. Кроме того, сбор мусора в LOH осуществляется только при полном сборе мусора.
5. IL-код Как мы знаем код C#, а так же VB.NET, и других .NET языков компилируется не в машинные инструкции, а в так называемый промежуточный байт-код, описываемый на языке MSIL. Подробное описание инструкций IL-ассемблера можно нати на msdn в описании класса System.Reflection.Emit.Opcodes. Но следует знать, что там описаны только часть инструкций. Например, там нет описания инструкций no и break. Получить .*il файл можно через командную строку разработчика, предварительно перейдя cd в директорию в исполняемым файлом, и набрав Код:
ildasm file.exe /out=file.il Существуют так же способы отладки непосредственно msil-кода 6. Инициализаторы модулей В .NET помимо статических конструкторов класса, можно так же задавать и статические конструкторы модулей. К сожалению, в C# такой возможности нет, но к C#-проекту можно прикрутить статический конструктор модуля через ilasm. Его сигнатура будет выглядеть так:
7. Деструкторы В C#, как и в C++, есть деструкторы. Но играют совсем иную роль: разумеется, их нельзя вызвать принудительно, т.к. сборкой мусора в .NET занимается сборщик мусора. Но тем не менее, их можно использовать как событие, оповещающее о том, что объект удален GC, вместо проверки слабых ссылок 8. try/catch/finally Еще одной интересной особенностью C# и .NET в частности, является то, что для блок try/catch не имеет опкодов для реализации. Они задаются не через инструкции il, а в специальных блоках Exception внутри тела метода. Эти блоки не влияют на CodeSize метода! И тем удивительнее, что finally не указываются в этих блоках: для него есть специальный опкод endfinally, по достижении которого передается управление обработчику исключений для прыжка на следующую после обработки блока инструкцию. Не по теме: Зная особенности такой конструкции, можно выполнять запутывание кода, на мой взгляд, похлеще, чем на чистом asm (но, конечно, местами в ущерб производительности). Тем более, что нормальных отладчиков msil-кода не существует 9. Удивительная компиляция foreach При пересборке своего проекта столкнулся с удивительной ситуацией:
get_Count() - получаем folds.Count. ldc.i4.0 - загружаем в стек ноль. Далее
Далее применяем GetEnumerator к folds:
V_30 . И далее снова идет инструкция br.s:
выполняет переход к конечной инструкции с указанным смещением . Вот в этой строке и начинается цикл. Переброс происходит на инструкцию 587 (L_07f4). Эта инструкция загружает Enumerator, чтобы применить к нему MoveNext() . Следующая за тем инструкция brtrue.s:Цитата:
Передает управление конечной инструкции (короткая форма), если value является true, не равно null или ненулевое значение.
Но вот если MoveNext вернет 0, то управление перейдет к инструкции leave.s, бросающей нас на № 595 И если у вас уже терпение на исходе и вы не понимаете, что особенного в этом разборе, то хочу вас обрадовать. Мы дошли до той самой точки, которой я хотел поделиться. Это строки:
Что же в них происходит? Метод constrained ограничивает тип, для которого был вызван виртуальный метод, а Dispose() освобождает ресурсы. Опкод endfinally - это End finally clause of an exception block . Что за ерунда? Не ерундаОказывается, к каждому блоку foreach cil-компилятор добавляет автоматически try-блок. В частности для приведенного выше отрывка к методу был сформирован .try1:
10. Получение свойств родительских (реализованных) интерфейсов Об этом уже много написано и кому надо, тот знает. Но на всякий случай напишу Мы все знаем метод GetProperties(). У класса он позволяет получить все public свойства static и instance характера (соответствует BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public параметрам). При чем у класса вернет все свойства, в т.ч. и наследуемые. Чтобы это не произошло, надо вызвать с параметром DeclaredOnly. У интерфейсов получить свойства родительского интерфейса нельзя. Например:
typeof(A).GetInterfaces() вернет только dДля того, чтобы получить все свойства, используют такой подход:
|
Всего комментариев 5
Комментарии
-
Цитата:В отличие от C++, где указатель на void является своеобразным универсальным указателем, в C# нельзя разыменовать void: *void, несмотря на то, что он является значимым типом
Во вторых в С++ void* тоже не удастся разыменовать, можно привести его как другому типу указателя и потом разыменовать.Запись от Avazart размещена 08.05.2018 в 12:57 -
Запись от netBool размещена 08.05.2018 в 17:34 -
Запись от Avazart размещена 08.05.2018 в 18:02 -
Запись от Rius размещена 08.05.2018 в 19:10 -
Цитата:Это вовсе не круто.
Цитата:Те, кому это надо, используют сразу C/C++, а не эти костыли в виде unsafe.
PS: Я на самом деле нисколько не фанат unsafe-C# в рабочих проектах. Скорее интересны эти возможности для личного велосипедирования и спортивного интересаЗапись от netBool размещена 09.05.2018 в 11:44