Execution-Time Code Generation in C#: A CodeDOM Implementation

A trip through the .NET Framework documentation shows a set of classes referred to as the CodeDOM. Visual Studio .NET designers use the CodeDOM to write the code for Windows Forms and Web Forms.

You can use a similar technique to generate a custom class for this example:

using System;

using System.IO;

using System.Diagnostics;

using System.Reflection;

using PolyInterface;

using System.CodeDom;

using System.CodeDom.Compiler;

using Microsoft.CSharp;

class PolyCodeDom: Polynomial

{

public PolyCodeDom(params double[] coefficients): base(coefficients)

{

}

void WriteCode()

{

string timeString = polyNumber.ToString();

polyNumber++;

 

string filename = “PSCD_” + timeString;

Stream s = File.Open(filename + “.cs”, FileMode.Create);

StreamWriter t = new StreamWriter(s);

 

// Generate code in C#

CSharpCodeProvider provider = new CSharpCodeProvider();

ICodeGenerator cg = provider.CreateGenerator(t);

CodeGeneratorOptions op = new CodeGeneratorOptions();

 

// Generate the comments at the beginning of the function

CodeCommentStatement comment =

new CodeCommentStatement(“Polynomial evaluator”);

cg.GenerateCodeFromStatement(comment, t, op);

 

string[] terms = new string[coefficients.Length];

terms[0] = coefficients[0].ToString();

 

for (int i = 1; i < coefficients.Length; i++)

terms[i] = String.Format(“{0} X^{1}”, coefficients[i], i);

comment = new CodeCommentStatement(

“Evaluating Y = ” + String.Join(” + “, terms));

cg.GenerateCodeFromStatement(comment, t, op);

// The class is named with a unique name

string className = “Poly_” + timeString;

CodeTypeDeclaration polyClass = new CodeTypeDeclaration(className);

// The class implements IPolynomial

polyClass.BaseTypes.Add(“PolyInterface.IPolynomial”);

// Set up the Eval function

CodeParameterDeclarationExpression param1 =

new CodeParameterDeclarationExpression(“double”, “x”);

CodeMemberMethod eval = new CodeMemberMethod();

eval.Name = “Evaluate”;

eval.Parameters.Add(param1);

 

// work-around for bug below…

eval.ReturnType = new CodeTypeReference(“public double”);

// BUG: This doesn’t generate “public”, it just leaves

// the attribute off of the member…

eval.Attributes |= MemberAttributes.Public;

// Create the expression to do the evaluation of the

// polynomial. To do this, we chain together binary

// operators to get the desired expression

// a0 + x * (a1 + x * (a2 + x * (a3)));

//

// This is very much like building a parse tree for

// an expression.

CodeBinaryOperatorExpression plus = new CodeBinaryOperatorExpression();

plus.Left = new CodePrimitiveExpression(coefficients[0]);

plus.Operator = CodeBinaryOperatorType.Add;

CodeBinaryOperatorExpression current = plus;

for (int i = 1; i < coefficients.Length; i++)

{

CodeBinaryOperatorExpression multiply = new CodeBinaryOperatorExpression();

current.Right = multiply;

multiply.Left = new CodeSnippetExpression(“x”); multiply.Operator =

CodeBinaryOperatorType.Multiply;

CodeBinaryOperatorExpression add = new CodeBinaryOperatorExpression();

multiply.Right = add;

add.Operator = CodeBinaryOperatorType.Add;

add.Left = new CodePrimitiveExpression(coefficients[i]);

current = add;

}

current.Right = new CodePrimitiveExpression(0.0);

// return the expression…

eval.Statements.Add(new CodeMethodReturnStatement(plus));

polyClass.Members.Add(eval);

cg.GenerateCodeFromType(polyClass, t, op);

 

t.Close();

s.Close();

 

//compile the DLL

CompilerParameters compParams = new CompilerParameters();

compParams.CompilerOptions = “/target:library /o+”;

compParams.ReferencedAssemblies.AddRange(new string[]

{Path.GetFileName(Assembly.GetExecutingAssembly().CodeBase),

“mscorlib.dll”,

“System.dll”});

compParams.IncludeDebugInformation = false;

compParams.GenerateInMemory = false;

compParams.OutputAssembly = (filename + “.dll”);

CompilerResults res = provider.CompileAssemblyFromFile(compParams,

filename + “.cs”);

// Open the file, create the instance, and cast it

// to the assembly

Assembly a = Assembly.LoadFrom(filename + “.dll”);

polynomial = (IPolynomial) a.CreateInstance(className);

File.Delete(filename + “.cs”);

}

public override IPolynomial GetEvaluate()

{

if (polynomial == null)

WriteCode();

return((IPolynomial) polynomial);

}

public override double Evaluate(double value)

{

return(0.0);                       // not used

}

IPolynomial polynomial = null;

static int polyNumber = 1000;

}

Because the approach is the same, this technique yields similar performance.

Source: Gunnerson Eric, Wienholt Nick (2005), A Programmer’s Introduction to C# 2.0, Apress; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *