| Рабочим названием платформы .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, который в дальнейшем может быть использован для вычисления значения функции в процессе интегрирования.