На заметку! Как видите, язык CIL поддерживает синтаксис комментариев в виде двойной косой черты (и вдобавок синтаксис
/*...*/
). Подобно компилятору C# компилятор CIL игнорирует
комментарии в коде.
Теперь, когда вы знаете основы директив, атрибутов и кодов операций CIL, давайте приступим к практическому программированию на CIL, начав с рассмотрения темы возвратного проектирования.
Возвратное проектирование
В главе 1 было показано, как применять утилиту
ildasm.exe
для просмотра кода CIL, сгенерированного компилятором С#. Тем не менее, вы можете даже не подозревать, что эта утилита позволяет сбрасывать код CIL, содержащийся внутри загруженной в нее сборки, во внешний файл. Полученный подобным образом код CIL можно редактировать и компилировать заново с помощью компилятора CIL (
ilasm.exe
).
Выражаясь формально, такой прием называется возвратным проектированием и может быть полезен в избранных обстоятельствах, которые перечислены ниже.
• Вам необходимо модифицировать сборку, исходный код которой больше не доступен.
• Вы работаете с далеким от идеала компилятором языка .NET Core, который генерирует неэффективный (или явно некорректный) код CIL, поэтому нужно изменять кодовую базу.
• Вы конструируете библиотеку взаимодействия с СОМ и хотите учесть ряд атрибутов COM IDL, которые были утрачены во время процесса преобразования (такие как COM-атрибут
[helpstring]
).
Чтобы ознакомиться с процессом возвратного проектирования, создайте новый проект консольного приложения .NET Core на языке C# по имени
RoundTrip
посредством интерфейса командной строки .NET Core (CLI):
Модифицируйте операторы верхнего уровня, как показано ниже:
// Простое консольное приложение С#.
Console.WriteLine("Hello CIL code!");
Console.ReadLine;
Скомпилируйте программу с применением интерфейса CLI:
dotnet build
На заметку! Вспомните из главы 1, что результатом компиляции всех сборок .NET Core (библиотек классов и консольных приложений) будут файлы с расширением
*.dll
, которые выполняются с применением интерфейса .NET Core CLI. Нововведением .NET Core 3+ (и последующих версий) является то, что файл
dotnet.exe
копируется в выходной каталог и переименовывается согласно имени сборки. Таким образом, хотя выглядит так, что ваш проект был скомпилирован в
RoundTrip.exe
, на самом деле он компилируется в
RoundTrip.dll
, а файл
dotnet.exe
копируется в
RoundTrip.exe
вместе с обязательными аргументами командной строки, необходимыми для запуска
Roundtrip.dll
.
Запустите
ildasm.exe
в отношении
RoundTrip.dll
, используя следующую команду (на уровне каталога решения):
На
заметку! При сбрасывании содержимого сборки в файл утилита
ildasm.exe
также генерирует файл
*.res
. Такие ресурсные файлы можно игнорировать (и удалять), поскольку в текущей главе они не применяются. В них содержится низкоуровневая информация, касающаяся безопасности CLR (помимо прочих данных).
Теперь можете просмотреть файл
RoundTrip.il
в любом текстовом редакторе. Вот его содержимое (для удобства оно слегка переформатировано и снабжено комментариями):
// Ссылаемые сборки.
.assembly extern System.Runtime
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A)
.ver 5:0:0:0
}
.assembly extern System.Console
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
.ver 5:0:0:0
}
// Наша сборка.
.assembly RoundTrip
{
...
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module RoundTrip.dll
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
.corflags 0x00000001
// Определение класса Program.
.class private abstract auto ansi beforefieldinit '<Program>$'
начинается с объявления всех внешних сборок, на которые ссылается текущая скомпилированная сборка. Если бы в вашей библиотеке классов использовались дополнительные типы из других ссылаемых сборок (помимо
System.Runtime
и
System.Console
), тогда вы обнаружили бы дополнительные директивы