, описанное с применением разнообразных директив CIL (
.module
,
.imagebase
и т.д.).
После документирования внешних ссылаемых сборок и определения текущей сборки находится определение типа
Program
, созданное из операторов верхнего уровня. Обратите внимание, что директива
.class
имеет различные атрибуты (многие из которых необязательны) вроде приведенного ниже атрибута
extends
, который указывает базовый класс для типа:
.class private abstract auto ansi beforefieldinit '<Program>$'
extends [System.Runtime]System.Object
{ ... }
Основной
объем кода CIL представляет реализацию стандартного конструктора класса и автоматически сгенерированного метода
Main
, которые оба определены (частично) посредством директивы
.method
. После того, как эти члены были определены с использованием корректных директив и атрибутов, они реализуются с применением разнообразных кодов операций.
Важно понимать, что при взаимодействии с типами .NET Core (такими как System.Console) в CIL всегда необходимо использовать полностью заданное имя типа. Более того, полностью заданное имя типа всегда должно предваряться префиксом в форме дружественного имени сборки, где определен тип (в квадратных скобках). Взгляните на следующую реализацию метода
Вы определенно заметили, что каждая строка в коде реализации предваряется лексемой в форме
IL_X
XX: (например,
IL_0000:
,
IL_0001:
и т.д.). Такие лексемы называются метками кода и могут именоваться в любой выбранной вами манере (при условии, что они не дублируются внутри области действия члена). При сбросе содержимого сборки в файл утилита
ildasm.exe
автоматически генерирует метки кода, которые следуют соглашению об именовании вида
IL_XXXX:
. Однако их можно заменить более описательными маркерами, например:
сущности, большая часть меток кода совершенно не обязательна. Единственный случай, когда метки кода по-настоящему необходимы, связан с написанием кода CIL, в котором используются разнообразные конструкции ветвления или организации циклов, т.к. с помощью меток можно указывать, куда должен быть направлен поток логики. В текущем примере все автоматически сгенерированные метки кода можно удалить безо всяких последствий:
Теперь, когда вы имеете представление о том, из чего состоит базовый файл CIL, давайте завершим эксперимент с возвратным проектированием. Цель здесь довольно проста: изменить сообщение, которое выводится в окно консоли. Вы можете делать что-то большее, скажем, добавлять ссылки на сборки или создавать новые классы и методы, но мы ограничимся простым примером.
Чтобы внести изменение, вам понадобится модифицировать текущую реализацию операторов верхнего уровня, созданную в виде метода
<Main>$
. Отыщите этот метод в файле
*.il
и измените сообщение на
"Hello from altered CIL code!"
.
Фактически код CIL был модифицирован для соответствия следующему определению на языке С#:
static void Main(string[] args)
{
Console.WriteLine("Hello from altered CIL code!");
Console.ReadLine;
}
Компиляция кода CIL
Предшествующие версии .NET позволяли компилировать файлы
*.il
с применением утилиты ilasm.exe. В .NET Core положение дел изменилось. Для компиляции файлов
*.il
вы должны использовать тип проекта
Microsoft.NET.Sdk.IL
. На момент написания главы он все еще не был частью стандартного комплекта SDK.
Начните с создания нового каталога на своей машине. Создайте в этом каталоге файл
global.json
, который применяется к текущему каталогу и всем его подкаталогам. Он используется для определения версии комплекта SDK, которая будет задействована при запуске команд .NET Core CLI. Модифицируйте содержимое файла, как показано ниже: