Запустите
ildasm.exe
в отношении
RoundTrip.dll
, используя следующую команду (на уровне каталога решения):
ildasm /all /METADATA /out=.\RoundTrip\RoundTrip.il
.\RoundTrip\bin\Debug\net5.0\RoundTrip.dll
На заметку! При сбрасывании содержимого сборки в файл утилита
ildasm.exe
также генерирует файл
*.res
. Такие ресурсные файлы можно игнорировать (и удалять), поскольку в текущей главе они не применяются. В них содержится низкоуровневая информация, касающаяся безопасности CLR (помимо прочих данных).
Теперь можете просмотреть файл
RoundTrip.il
в любом текстовом редакторе. Вот его содержимое (для удобства оно слегка переформатировано и снабжено комментариями):
<b>// Ссылаемые сборки.</b>
.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
}
<b>// Наша сборка.</b>
.assembly RoundTrip
{
...
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module RoundTrip.dll
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
.corflags 0x00000001
<b>// Определение класса Program.</b>
.class private abstract auto ansi beforefieldinit '<Program>$'
extends [System.Runtime]System.Object
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices
.
CompilerGeneratedAttribute::.ctor()
= ( 01 00 00 00 )
.method private hidebysig static void '<Main>$'(string[] args) cil managed
{
<b> // Помечает этот метод как точку входа исполняемой сборки.</b>
.entrypoint
.maxstack 8
IL_0000: ldstr "Hello CIL code!"
IL_0005: call void [System.Console]System.Console::WriteLine(string)
IL_000a: nop
IL_000b: call string [System.Console]System.Console::ReadLine()
IL_0010: pop
IL_0011: ret
} // end of method '<Program>$'::'<Main>$'
} // end of class '<Program>$'
Обратите внимание, что файл
*.il
начинается с объявления всех внешних сборок, на которые ссылается текущая скомпилированная сборка. Если бы в вашей библиотеке классов использовались дополнительные типы из других ссылаемых сборок (помимо
System.Runtime
и
System.Console
), тогда вы обнаружили бы дополнительные директивы
.assembly extern
.
Далее следует формальное определение сборки
RoundTrip.dll
, описанное с применением разнообразных директив 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 всегда необходимо использовать полностью заданное имя типа. Более того, полностью заданное имя типа всегда должно предваряться префиксом в форме дружественного имени сборки, где определен тип (в квадратных скобках). Взгляните на следующую реализацию метода
Main()
в CIL:
.method private hidebysig static void '<Main>$'(string[] args) cil managed
{
<b> // Помечает этот метод как точку входа исполняемой сборки.</b>
.entrypoint
.maxstack 8
IL_0000: ldstr "Hello CIL code!"
IL_0005: call void [System.Console]System.Console::WriteLine(string)
IL_000a: nop
IL_000b: call string [System.Console]System.Console::ReadLine()
IL_0010: pop