Рабочим названием платформы .NET было |
Динамическая генерация кода
Трансляция выражений в C#
Как уже говорилось, самым простым способом динамической генерации кода является порождение текста C#-программы и компиляция этой программы с помощью компилятора C#, доступного через библиотеку классов .NET.
В нашем примере динамическую генерацию сборки осуществляет статический метод CompileToCS, который получает транслируемое выражение в виде объекта класса Expression и возвращает объект Function:
static Function CompileToCS(Expression expr) { ICodeCompiler compiler = new CSharpCodeProvider().CreateCompiler(); CompilerParameters parameters = new CompilerParameters(); parameters.ReferencedAssemblies.Add("System.dll"); parameters.ReferencedAssemblies.Add("Integral.exe"); parameters.GenerateInMemory = true; string e = expr.GenerateCS(); string code = "public class FunctionCS: Function\n"+ "{\n"+ " public override double Eval(double x)\n"+ " {\n"+ " return "+e+";\n"+ " }\n"+ "}\n"; CompilerResults compilerResults = compiler.CompileAssemblyFromSource(parameters,code); Assembly assembly = compilerResults.CompiledAssembly; return assembly.CreateInstance("FunctionCS") as Function; }
Классы, отвечающие за компиляцию исходного кода, относятся к пространству имен System.CodeDom.Compiler.
Основную функциональность, необходимую нам для компиляции сгенерированной C#-программы, обеспечивает класс CSharpCodeProvider. Метод CreateCompiler этого класса создает экземпляр компилятора C#, к которому можно обращаться через интерфейс ICodeCompiler.
Параметры компиляции задаются через объект класса CompilerParameters. В нашем случае это имена сборок, импортируемых генерируемой программой: System.dll и Integral.exe. Обратите внимание, что Integral.exe - это сборка, получаемая при компиляции рассматриваемого нами примера. Она импортируется по причине того, что динамически генерируемый класс FunctionCS должен наследовать от определенного в ней абстрактного класса Function.
Параметры компиляции и строка, содержащая текст программы, передаются методу CompileAssemblyFromSource экземпляра компилятора C#. Метод компилирует программу и возвращает объект класса CompilerResults, содержащий результаты компиляции. Из этого объекта мы можем получить объект рефлексии Assembly, представляющий созданную в памяти динамическую сборку. Используя данный объект, мы создаем экземпляр определенного в динамической сборке класса FunctionCS, который в дальнейшем может быть использован для вычисления значения функции в процессе интегрирования.