Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

stloc.0

// Загрузить значение по индексу 0.

ldloc.0

// Вызвать метод с текущим значением.

call void [System.Console]System.Console::WriteLine(string)

ret

}

На заметку! Как видите, язык 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):

dotnet new console -lang c# -n RoundTrip -o .\RoundTrip -f net5.0

Модифицируйте операторы верхнего уровня, как показано ниже:

// Простое консольное приложение С#.

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 /all /METADATA /out=.\RoundTrip\RoundTrip.il

.\RoundTrip\bin\Debug\net5.0\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>$'

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

{

// Помечает этот метод как точку входа исполняемой сборки.

.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
.

Поделиться с друзьями: