Common Intermediate Language и системное программирование в Microsoft.Net. Макаров А.В - 91 стр.

UptoLike

ConstructorBuilder cons =
typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { }
);
ILGenerator consIl = cons.GetILGenerator();
consIl.Emit(OpCodes.Ldarg_0);
consIl.Emit(OpCodes.Call,
typeof(object).GetConstructor(new Type[0]));
consIl.Emit(OpCodes.Ret);
Разобравшись с конструктором, переходим к методу Eval. Как уже
говорилось, код этого метода почти полностью генерируется в методе
GenerateCIL выражения, остается лишь добавить в конец инструкцию ret:
MethodBuilder evalMethod =
typeBuilder.DefineMethod(
“Eval”,
MethodAttributes.Public | MethodAttributes.Virtual,
typeof(double),
new Type[] { typeof(double) }
);
ILGenerator il = evalMethod.GetILGenerator();
expr.GenerateCIL(il);
il.Emit(OpCodes.Ret);
Итак, мы закончили формирование класса FunctionCIL. Осталось со-
здать для него объект рефлексии и через этот объект вызвать конструктор:
Type type = typeBuilder.CreateType();
ConstructorInfo ctor = type.GetConstructor(new Type[0]);
return ctor.Invoke(null) as Function;
Таким образом, получается объект класса FunctionCIL, который в
дальнейшем можно использовать для вычисления значения функции в
процессе интегрирования.
5.1.5. Сравнение эффективности трех способов вычисления
выражений
Давайте оценим эффективность рассмотренных способов вычисле-
ния выражений при интегрировании. Для этого будем интегрировать
функцию «2*x*x*x+3*x*x+4*x+5» от 0.0 до 10.0 с 10000000 разбиений.
В таблице 5.1 представлены результаты измерений, проведенных на
компьютере с процессором Intel Pentium 4 с тактовой частотой 3000 МГц и
1 Гб оперативной памяти.
Динамическая генерация кода
169
5.1.4. Трансляция выражений в CIL
Более сложный, но эффективный способ динамической генерации
кода предоставляется классами, относящимися к пространству имен
System.Reflection.Emit. Эти классы в нашем примере используются в ста-
тическом методе CompileToCIL, который осуществляет трансляцию выра-
жения напрямую в CIL:
static Function CompileToCIL(Expression expr)
Метод начинается с создания заготовки для будущей динамической
сборки. Сборка будет выполняться в том же домене приложений, что и ос-
новная программа, поэтому объектную ссылку на домен приложений мы
получаем путем вызова статического метода Thread.GetDomain. Затем вы-
зываем метод DefineDynamicAssembly домена приложений и получаем объ-
ект класса AssemblyBuilder, позволяющий строить динамическую сборку:
AppDomain appDomain = Thread.GetDomain();
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = “f”;
AssemblyBuilder assembly =
appDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.RunAndSave
);
Теперь мы можем создать в сборке модуль и добавить в него класс
FunctionCIL, наследующий от класса Function. Обратите внимание, что
при генерации кода через классы пространства имен
System.Reflection.Emit явно прописывать импортируемые сборки не надо
(например, не надо прописывать сборку Integral.exe, из которой импор-
тируется класс Function), так как это выполняется автоматически:
ModuleBuilder module =
assembly.DefineDynamicModule(“f.dll”, “f.dll”);
TypeBuilder typeBuilder =
module.DefineType(
“FunctionCIL”,
TypeAttributes.Public | TypeAttributes.Class,
typeof(Function)
);
В каждом классе должен быть конструктор. Компилятор C# создает
конструкторы без параметров по умолчанию, поэтому при генерации C#-
кода нам не надо было явно объявлять конструктор в классе FunctionCS.
Однако, при генерации динамической сборки через классы пространства
имен System.Reflection.Emit конструкторы автоматически не добавляют-
ся, и нам придется сделать это самостоятельно:
168
CIL и системное программирование в Microsoft .NET
168                        CIL и системное программирование в Microsoft .NET   Динамическая генерация кода                                          169


5.1.4. Трансляция выражений в CIL                                                     ConstructorBuilder cons =
                                                                                        typeBuilder.DefineConstructor(
      Более сложный, но эффективный способ динамической генерации                          MethodAttributes.Public,
кода предоставляется классами, относящимися к пространству имен                            CallingConventions.Standard,
System.Reflection.Emit. Эти классы в нашем примере используются в ста-                     new Type[] { }
тическом методе CompileToCIL, который осуществляет трансляцию выра-                     );
жения напрямую в CIL:                                                                 ILGenerator consIl = cons.GetILGenerator();
      static Function CompileToCIL(Expression expr)                                   consIl.Emit(OpCodes.Ldarg_0);
      Метод начинается с создания заготовки для будущей динамической                  consIl.Emit(OpCodes.Call,
сборки. Сборка будет выполняться в том же домене приложений, что и ос-                  typeof(object).GetConstructor(new Type[0]));
новная программа, поэтому объектную ссылку на домен приложений мы                     consIl.Emit(OpCodes.Ret);
получаем путем вызова статического метода Thread.GetDomain. Затем вы-               Разобравшись с конструктором, переходим к методу Eval. Как уже
зываем метод DefineDynamicAssembly домена приложений и получаем объ-           говорилось, код этого метода почти полностью генерируется в методе
ект класса AssemblyBuilder, позволяющий строить динамическую сборку:           GenerateCIL выражения, остается лишь добавить в конец инструкцию ret:
        AppDomain appDomain = Thread.GetDomain();                                     MethodBuilder evalMethod =
        AssemblyName assemblyName = new AssemblyName();                                 typeBuilder.DefineMethod(
        assemblyName.Name = “f”;                                                           “Eval”,
        AssemblyBuilder assembly =                                                         MethodAttributes.Public | MethodAttributes.Virtual,
           appDomain.DefineDynamicAssembly(                                                typeof(double),
              assemblyName,                                                                new Type[] { typeof(double) }
              AssemblyBuilderAccess.RunAndSave                                          );
           );
      Теперь мы можем создать в сборке модуль и добавить в него класс                 ILGenerator il = evalMethod.GetILGenerator();
FunctionCIL, наследующий от класса Function. Обратите внимание, что                   expr.GenerateCIL(il);
при      генерации      кода    через    классы    пространства  имен                 il.Emit(OpCodes.Ret);
System.Reflection.Emit явно прописывать импортируемые сборки не надо                Итак, мы закончили формирование класса FunctionCIL. Осталось со-
(например, не надо прописывать сборку Integral.exe, из которой импор-          здать для него объект рефлексии и через этот объект вызвать конструктор:
тируется класс Function), так как это выполняется автоматически:                     Type type = typeBuilder.CreateType();
        ModuleBuilder module =                                                       ConstructorInfo ctor = type.GetConstructor(new Type[0]);
           assembly.DefineDynamicModule(“f.dll”, “f.dll”);                           return ctor.Invoke(null) as Function;
        TypeBuilder typeBuilder =                                                   Таким образом, получается объект класса FunctionCIL, который в
           module.DefineType(                                                  дальнейшем можно использовать для вычисления значения функции в
              “FunctionCIL”,                                                   процессе интегрирования.
              TypeAttributes.Public | TypeAttributes.Class,
              typeof(Function)                                                 5.1.5. Сравнение эффективности трех способов вычисления
           );                                                                  выражений
      В каждом классе должен быть конструктор. Компилятор C# создает                Давайте оценим эффективность рассмотренных способов вычисле-
конструкторы без параметров по умолчанию, поэтому при генерации C#-            ния выражений при интегрировании. Для этого будем интегрировать
кода нам не надо было явно объявлять конструктор в классе FunctionCS.          функцию «2*x*x*x+3*x*x+4*x+5» от 0.0 до 10.0 с 10000000 разбиений.
Однако, при генерации динамической сборки через классы пространства                 В таблице 5.1 представлены результаты измерений, проведенных на
имен System.Reflection.Emit конструкторы автоматически не добавляют-           компьютере с процессором Intel Pentium 4 с тактовой частотой 3000 МГц и
ся, и нам придется сделать это самостоятельно:                                 1 Гб оперативной памяти.