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

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

а затем проверить полученный CIL-код с помощью peverifу.exe.

peverify CILCars.dll

Создание CILCarClient.exe

Теперь нам нужно построить простой компоновочный блок *.exe, который должен выполнить следующее.

• Создать тип CILCar.

• Передать этот тип статическому методу CILCarInfo.Display,

Создайте новый файл *.il и определите внешние ссылки на mscorlib.dll и CILCars.dll (не забудьте поместить копию этого компоновочного блока .NET в каталог приложения клиента!). Затем определите единственный тип (Program), который использует компоновочный блок CILCars.dll. Вот соответствующий программный код, приведенный полностью.

// Ссылки
на внешние компоновочные блоки.

.assembly extern mscorlib {

 .publickeytoken = (B7 7A 5C 56 19 34 E0 89)

 .ver 2:0:0:0

}

.assembly extern CILCars {

 .ver 1:0:0:0

}

// Наш выполняемый компоновочный блок.

.assembly CILCarClient {

 .hash algorithm 0x00008004

 .ver 0:0:0:0

}

.module CILCarClient.exe

// Реализация типа Program.

.namespace CILCarClient {

 .class private auto ansi beforefieldinit Program extends [mscorlib]System.Object {

.method private hidebysig static void Main(string[] args) cil managed {

// Обозначает точку входа *.exe.

.entrypoint

.maxstack 8

// Объявление локального типа CILCar и добавление в стек

// значений для вызова конструктора.

.locals init ([0] class [CILCars]CILCars.CILCar myCilCar)

ldc.i4 55

ldstr "Junior"

// Создание нового CILCar: сохранение и загрузка ссылки.

newobj: instance void [CILCars] CILCars.CILCar::.сtor(int32, string)

stloc.0

ldloc.0

// Вызов Display и передача верхнего значения из стека.

call void [CILCars] CILCars.CILCarInfo::Display(class [CILCars]CILCars.CILCar)

ret

}

 }

}

Здесь единственным кодом операции, заслуживающим комментария, является .entrуpoint. Напомним, что этот код операций используется для обозначения метода, который должен выступать в качестве точки входа модуля *.eхе. Ввиду того, что среда CLR идентифицирует начальный метод для выполнения именно с помощью.entrypoint, сам метод может называться как угодно (хотя в нашем примере он называется Main). В остальном CIL-код метода Main представляет действия, связанные с добавлением значений в стек и извлечением их из стека.

Заметьте, однако, что для создания CILCar используется код операции.newobj. В связи с этим напомним, что при вызове члена типа непосредственно в CIL вы должны применить синтаксис с использованием двойного двоеточия и, как всегда, указать абсолютное имя типа. Восприняв сказанное, вы можете скомпилировать свой новый файл с помощью ilasm.exe, проверить полученный компоновочный блок с помощью peverifу.exe, а затем выполнить программу.

ilasm CilCarClient.il

peverify CilCarClient.exe

CILCarClient.exe

На рис. 15.5 показан конечный результат.

Рис. 15.5.

Ваш CILCar в действии

На этом, выполнив первую задачу этой главы, мы закончим освоение азбуки CIL. К этому моменту, я надеюсь, вы уверенно сможете открыть любой компоновочный блок .NET с помощью ildasm.exe и лучше понимаете, что происходит внутри него.

Динамические компоновочные блоки

Как видите, процесс создания сложного приложения .NET непосредственно в CIL оказывается довольно трудоемким. С одной стороны, CIL является чрезвычайно выразительным языком программирования, позволяющим взаимодействовать со всеми программными конструкциями, допустимыми в CTS. С другой стороны, создание CIL-кода является делом скучным, утомительным и сопряженным с множеством ошибок. Хотя верно и то, что знание – это сила, вы можете поинтересоваться, действительно ли это так важно, чтобы "загромождать" законами синтаксиса CIL свою память. Я отвечу так: это зависит от многого. Конечно, для большинства ваших .NET-проектов рассматривать, редактировать или непосредственно создавать программный код CIL не потребуется. Но, освоив азбуку CIL, вы теперь готовы к обсуждению динамических компоновочных блоков (которые называются так в противоположность статическим компоновочным блокам) и роли пространства имен System.Reflection.Emit.

Здесь сразу же может возникнуть вопрос: "В чем разница между статическими и динамическими компоновочными блоками?" По определению, статические компоновочные блоки являются двоичными файлами .NET, загружаемыми по запросу CLR непосредственно с диска (в предположении о том, что они размещены где-то на вашем жестком диске в физическом файле или, возможно, во множестве файлов, если компоновочный блок является многомодульным). Как вы можете догадаться сами, каждый раз, когда вы компилируете исходный код C#, вы получаете статический компоновочный блок.

Динамический компоновочный блок, с другой стороны, создается в памяти "на лету", с использованием типов, предлагаемых пространством имен System. Reflection.Emit. Пространство имен System.Reflection.Emit делает возможным создание компоновочного блока и его модулей, определений типов и логики реализации CIL прямо в среде выполнения. Создав компоновочный блок таким образом, вы можете сохранить свой находящийся в памяти двоичный объект на диск. В результате, конечно, получится новый статический компоновочный блок. Для понимания процесса построения динамического компоновочного блока с помощью пространства имен System.Reflection.Emit требуется определенный уровень понимания кодов операций CIL.

Конечно, создание динамических компоновочных блоков является довольно сложным (и не слишком часто применяемым) приемом программирования, но этот подход может оказаться полезным в следующих обстоятельствах,

• При создании инструментов программирования .NET. позволяющих по требованию динамически генерировать компоновочные блоки в зависимости от пользовательского ввода.

• При создании программ, способных динамически генерировать агенты доступа к удаленным типам на основе получаемых метаданных.

• При загрузке статических компоновочных блоков с динамическим добавлением новых типов в двоичный образ.

Учитывая сказанное, давайте рассмотрим типы, предлагаемые в System.Reflection.Emit.

Исследование пространства имен System.Reflection.Emit

Для создания динамического компоновочного блока требуется в определенной мере понимать коды операций CIL, но типы пространства имен System.Reflection. Emit в максимальной мере "пытаются" скрыть сложность CIL. Например, вместо прямого указания необходимых директив и атрибутов CIL при определении типа класса вы можете просто использовать класс TypeBuilder. Точно так же, чтобы определить новый конструктор уровня экземпляра, нет никакой необходимости использовать specialname, rtspecialname и лексемы .ctor – вместо этого можно просто использовать ConstructorBuilder. Описания ключевых членов пространства имен System.Reflection.Emit приводятся в табл. 15.8.

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