Принципы объектно-ориентированного программирования

         

Создание и исполнение кода в период выполнения



Создание и исполнение кода в период выполнения

Вы уже видели, как отражать типы в период выполнения, осуществлять позднее связывание с кодом и динамическое исполнение кода. Сделаем следующий шаг в логической последовательности — рассмотрим создание кода «на лету». При создании типов в период выполнения используется пространство имен System.Reflection.Emit. С помощью классов из этого пространства имен можно определять сборку в памяти, создавать для нее модули, определять для модулей новые типы (и их члены) и даже генерировать коды операций MSIL для реализации прикладной логики.

Несмотря на чрезвычайную простоту кода этого, примера, я отделил серверный код — DLL, содержащую класс, который создает метод Hello-World, — от клиентского кода, приложения, которое создает экземпляр класса, генерирующего код, и вызывает его метод Hello World (обратите внимание на переключатели компилятора в комментариях). Объяснение этого кода DLL приводится ниже:

using System;



using System.Reflection;

using System.Reflection.Emit;

namespace ILGenServer {

public class CodeGenerator {

public CodeGenerator() {

// Получить текущий currentDomain. currentDomain = AppDomain.CurrentDomain;

// Создать сборку в текущем currentDomain. assemblyName = new AssemblyNameQ; assemblyName.Name = "TempAssembly";

assemblyBuilder =

currentDomain.DefineDynamicAssembly

(assemblyName, AssemblyBuilderAccess.Run);

// Создать в этой сборке модуль moduleBuilder = assemblyBuilder.DefineOynamicModule ("TempModule");

// Создать тип в этой сборке typeBuilder = moduleBuilder.DefineType ("TempClass", TypeAttributes.Public); // Добавить к типу член (метод) methodBuilder = typeBuilder.DefineMethod ("HelloWorld", MethodAttributes.Public, null,null);

// Генерировать MSIL.

msil = methodBuilder.GetlLGeneratorO;

rasil.EmitWriteLine("Hello World 11 );

msil.Emit(OpCodes.Ret);

// Последний шаг: создание типа, t = typeBuilder.CreateTypeO;

>

AppDomain currentDomain; AssemblyName assemblyName; AssemblyBuilder assemblyBuilder; ModuleBuilder moduleBuilder; TypeBuilder typeBuilder; MethodBuilder roethodBuilder; ILGenerator msil; object o;

Type t; public Type T {

get {

return this.t; } > } }

Сначала мы создали экземпляр объекта AppDomain из текущей области (в главе 17 вы увидите, что прикладные области в функциональном плане похожи на процессы Win32). После этого мы создали экземпляр объекта Assembly Name. Класс AssemblyName подробно описан в главе 18, а вообще этот класс используется диспетчером кэша сборки для получения информации о ней. Получив текущую прикладную область и инициализированное имя сборки, вызываем метод AppDomain.Defme-DynamicAssembly, чтобы создать новую сборку. Заметьте, что два передаваемые нами аргумента являются именем сборки и описанием режима доступа к ней. Assembly Builder Access.Run указывает, что сборка может быть исполнена из памяти, но не может быть сохранена. Метод AppDomain. Define DynamicAssembly возвращает объект Assembly Builder, который мы затем приводим к объекту Assembly. На этом этапе у нас есть полнофункциональная сборка в памяти. Теперь нам нужно создать ее временный модуль и его тип.

Начнем с вызова метода Assembly. DefineDynamicModule для получения объекта ModuleBuilder. Получив этот объект, мы вызываем его метод DefineType, чтобы создать объект ТуреВтШег, передавая методу имя типа (« TempClass») и используемые для его определения атрибуты ( TypeAttri-butes.Public). Теперь, имея объект TypeBuilder, можно создать член любого нужного нам типа. В данном случае мы создаем новый метод с помощью метода TypeBuilder. DefineMethod.

В результате получаем совершенно новый тип TempClass с встроенным методом HelloWorld. Теперь все, что нам осталось сделать, — это решить, какой код поместить в этот метод. Для этого код создает экземпляр объекта ILGenerator с помощью метода MethodBuilder.GetlLGenerator и вызывает различные методы IWenerator для записи в метод MSIL-кода.

Здесь мы можем использовать стандартный код типа Console. WriteLine с помощью различных методов IWenerator или генерировать коды операций MSIL, используя метод IWenerator. Emit. Метод IWenerator.Emit в качестве единственного аргумента принимает поле члена класса OpCodes, непосредственно связанное с кодом операции MSIL.

В завершение вызываем метод TypeBuilder.CreateType. Это действие всегда должно выполняться последним, после того как вы определили члены нового типа. Далее мы получаем объект Type для нового типа с помощью метода Type.GetType. Этот объект будет храниться в члене-переменной клиентского приложения, которому он впоследствии понадобится.

Теперь все, что осталось сделать клиенту, — это получить член Type класса CodeGenerator, создать экземпляр активатора, экземпляр объекта Methodlnfo из типа и затем вызвать метод. Вот код, выполняющий эти действия, к которому добавлена небольшая проверка на наличие ошибок, чтобы быть уверенным, что все работает как надо:

using System;

using System.Reflection;

using ILGenServer;

public class ILGenClientApp {

public static void Main() {

Console.WriteLine("Calling DLL function to generate " + "a new type and method in memory..."); CodeGenerator gen = new CodeGeneratorQ;

Console.WriteLine("Retrieving dynamically generated

type..."); Type t = gen.T; if (null != t) {

Console.WriteLine("Instantiating the new type...");

object о = Activator.Createlnstance(t);

Console.WriteLine("Retrieving the type's " +

"HelloWorld method...");

Methodlnfo helloWorld = t.GetMethod("HelloWorld"); if (null != helloWorld) {

Console.WriteLine("Invoking our dynamically " +

"created HelloWorld method..."); helloWorld.Invoke(o, null); }

else <

Console.WriteLine("Could not locate " + "HelloWorld method"); } >

else {

Console.WriteLin&("Could not access Type from server"); > > >

Скомпоновав и исполнив это приложение, вы увидите:

Calling DLL function to generate a new type and method in memory...

Retrieving dynamically generated type...

Instantiating the new type...

Retrieving the type's HelloWorld method...

Invoking our dynamically created HelloWorld method...

Hello World



Содержание раздела