Динамическая компиляция и загрузка кода - Динамическая компиляция средствами Reflection Emit

ОГЛАВЛЕНИЕ

Динамическая компиляция средствами Reflection Emit

Пространство имен Reflection.Emit предоставляет наборклассов для динамической генерации кода на промежуточном языке (MSIL).Неоспоримым преимуществом этой технологии перед динамической компиляциейисходного кода написанного на языках высокого уровня является скоростькомпиляции. Это, безусловно, важный фактор, хотя не каждый программист захочетизучать ради этого MSIL.

Интерес представляет также то,что можно использовать конструкции, недопустимые в языках высокого уровня.Например, можно при определении метода задать доступ к нему только дляпроизводных классов в той же сборке (MethodAttributes.FamANDAssem), в то времякак C# не позволяет задать такой модификатор доступа (“protected internal”соответствует MethodAttributes.FamORAssem).

Как правило, для динамическойкомпиляции используется следующая последовательность действий:

1. Создаетсядинамическая сборка (экземпляр класса AssemblyBuilder) с помощью метода AppDomain.DefineDynamicAssembly.

2. Создаетсямодуль (экземпляр класса ModuleBuilder) с помощью методаAssemblyBuilder.DefineDynamicModule.

3. Создаютсяэлементы модуля (TypeBuilder, EnumBuilder,..) с помощью методов классаModuleBuilder.

4. Еслинужно, сборка сохраняется на диск, с помощью метода AssemblyBuilder.Save.

5. Создаютсянужные экземпляры классов (получить соответствующий тип можно с помощью TypeBuilder.CreateType) и вызываютсяих методы посредством Reflection (если они не былиунаследованы от известных вызывающему коду базовых классов и не реализуютизвестные интерфейсы).

Получить более подробнуюинформацию об использовании ReflectionEmit можно в MSDN.

В .NET Framework 2.0 появилось много новыхвариантов использования Reflection Emit. Перечислю лишь некоторые из них:

•Генерация шаблонов.

•Использование MethodBody для получения содержимого метода

•Генерация и выполнение методов без создания динамической сборки.

Остановимся подробнее на генерациидинамических методов без создания сборки. Она позволяет создавать метод,глобальный для модуля и, помимо этого, не использовать проверку видимости(JIT compiler’visibility checks).Рассмотрим небольшой пример, вычисляющий значение логарифма (разумеется, примерне преследует цель найти самый легкий путь для вычисления логарифма). Нампотребуются следующие пространства имен:

using System;
using System.Reflection;
using System.Reflection.Emit;

Затем мы должны описать делегат,с помощью которого мы будем вызывать динамический метод:

private delegate doubleDynamicCalcDelegate(double x, double y); 

Теперь мы можем написать метод(например, Main), который будет создавать динамическийметод и вызывать его:

// задаем сигнатуру метода
DynamicMethod dynamicCalc = newDynamicMethod("DynamicCalc",
typeof(double),
new Type[] { typeof(double),typeof(double) },
typeof(Program));
// получаем описание стандартного метода для вычисления логарифма
MethodInfo log =typeof(Math).GetMethod("Log",
new Type[] { typeof(double),typeof(double) });
// генерируем реализацию метода
ILGenerator il = dynamicCalc.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Call, log, null);
il.Emit(OpCodes.Ret);
// получаем делегат и вызываем его
DynamicCalcDelegate dc =(DynamicCalcDelegate)dynamicCalc.CreateDelegate( typeof (DynamicCalcDelegate));
Console.WriteLine(dc(100.0, 10.0));

Естественно, наш код, генерирующий реализацию метода оченьпрост. Простота объясняется небольшим количеством кода и совпадением сигнатурметодов Math.Log и DynamicCalc. С другой стороны,пример наглядно демонстрирует, что написание динамических методов сравнительнопростая задача, если не принимать во внимание генерацию кода с помощьюILGenerator.

Автор: OlegAxenow