ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание
Шрифт:
Генерирование типа HelloClass и принадлежащей ему строковой переменной
Теперь вы понимаете роль метода ModuleBuilder.CreateType, и пришло время выяснить, как сгенерировать открытый тип класса HelloWorld и приватную строковую переменную.
Обратите внимание на то, что метод TypeBuilder.DefineField обеспечивает доступ к типу FieldBuilder. Класс TypeBuilder определяет также другие методы, обеспечивающие доступ к другим типам "построителя". Например, DefineConstructor возвращает ConstructorBuilder.DefineProperty – PropertyBuilder и т.д.
Генерирование конструкторов
Как уже упоминалось выше, для определения конструктора типа может использоваться метод TypeBuilder.DefineConstructor. Однако в нашей реализации конструктора HelloClass, чтобы назначить поступающий параметр внутренней приватной строке, мы добавим CIL-код в тело конструктора непосредственно. Чтобы получить тип ILGenerator, вызывается метод GetILGenerator соответствующего типа "построителя", на который имеется ссылка (в данном случае это тип ConstructorBuilder).
Метод Emit класса ILGenerator отвечает за размещение CIL-кода в реализации члена. Сам метод Emit часто использует тип класса OpCodes, который с помощью полей, доступных только для чтения, открывает доступ к набору кодов операций CIL. Например, OpCodes.Ret указывает возврат вызова метода, OpCodes.Stfld выполняет присваивание значения члену-переменной, a OpCodes.Call используется для вызова метода (в нашем случае это конструктор базового класса). С учетом сказанного рассмотрите следующую программную логику конструктора.
Вы, конечно, хорошо знаете, что как только для типа определяется пользовательский конструктор, конструктор, заданный по умолчанию, автоматически "отключается". Чтобы переопределить конструктор, не имеющий аргументов, просто вызовите метод DefineDefaultConstructor типа TypeBuilder, как показано ниже.
Следующий вызов порождает стандартный CIL-код для определения конструктора, заданного по умолчанию.
Генерирование метода HelloWorld
Наконец, рассмотрим задачу генерирования метода SayHello. Первой задачей здесь оказывается получение типа MethodBuilder из переменной helloWorld-Class. После этого определяется указанный метод и получается ILGenerator, позволяющий добавить соответствующие CIL-инструкции.
Здесь создается открытый метод (MethodAttributes.Public), не имеющий параметров и не возвращающий ничего (на это указывают значения null в вызове DefineMethod). Также обратите внимание на вызов EmitWriteLine. Этот вспомогательный член класса ILGenerator автоматически записывает строку в стандартный поток вывода.
Использование динамически сгенерированного компоновочного блока
Теперь, когда имеется вся программная логика, позволяющая создать и сохранить компоновочный блок, нужен класс для запуска этой логики. Для того чтобы замкнуть цикл, предположим, что в проекте определен второй класс, названный AsmReader. С помощью метода Thread.GetDomain в Main получается доступ к текущему домену приложения, который используется для принятия динамически создаваемого компоновочного блока. Получив соответствующую ссылку, вы можете вызвать метод CreateMyAsm.