Blog Archives

Reflection (part 5, professional), CodeDOM (& lambda expression tree)

Today’s post is about code generation at runtime using CodeDOM, which is the logical step before runtime code generation using Emit. The .Net framework has been built in an amazing way. We can easily neglect some flaws. Compared to Java there are no flaws at all. Jealousy needs to be deserved, sympathy is for free. Somehow Java is for free, and this is what you get.

Make sure, you are aware of the following differences. There are Expression and Statement. Please follow the links if you feel uncertain about the definitions.

Expression
An expression is a sequence of one or more operands and zero or more operators that can be evaluated to a single value, object, method, or namespace.

Statement
The actions that a program takes are expressed in statements. Common actions include declaring variables, assigning values, calling methods, looping through collections, and branching to one or another block of code, depending on a given condition. The order in which statements are executed in a program is called the flow of control or flow of execution.
A statement can consist of a single line of code that ends in a semicolon, or a series of single-line statements in a block. A statement block is enclosed in {} brackets and can contain nested blocks.

Succinct:
An Expression is a kind of mathematical or logical expression.
A Statement is a single code instruction or block of code to execute.

The first example code generates source code for C# and VB and saves the generated files on your desktop. Of course it has to be the classical “hello world” program.
CodeDOM stands for Code Document Object Model. It is located in the System.CodeDom namespace and provides methods to create code at runtime.

// what you need for the code example:
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;
using Microsoft.VisualBasic;

First of all you have to create a compile unit object CodeCompileUnit. It is the top container for namespaces, classes and members. Then add the components as follows:

public static void Test() {
    CodeCompileUnit lUnit = new CodeCompileUnit();

    CodeNamespace lNamespace = new CodeNamespace("MyNamespace");
    lNamespace.Imports.Add(new CodeNamespaceImport("System"));
    lNamespace.Imports.Add(new CodeNamespaceImport("System.IO"));     // not used, just for demo
    lUnit.Namespaces.Add(lNamespace);

    CodeTypeDeclaration lClass = new CodeTypeDeclaration("MyClass");
    CodeMethodInvokeExpression lExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Console"), "WriteLine", new CodePrimitiveExpression("hello world !"));
    lNamespace.Types.Add(lClass);

    // write Main entry point method
    CodeEntryPointMethod lMain = new CodeEntryPointMethod();
    lMain.Statements.Add(lExpression);
    lClass.Members.Add(lMain);

    // write another method
    CodeMemberMethod lMethod = new CodeMemberMethod();
    lMethod.Name = "DoSomething";
    lClass.Members.Add(lMethod);

    string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
    CodeGeneratorOptions lOptions = new CodeGeneratorOptions();
    lOptions.IndentString = "  "; // or "\t";
    lOptions.BlankLinesBetweenMembers = true;

    // generate a C# source code file
    CSharpCodeProvider lCSharpCodeProvider = new CSharpCodeProvider();
    using (StreamWriter lStreamWriter = new StreamWriter(lDesktopPath + "HelloWorld.cs", false)) {
        lCSharpCodeProvider.GenerateCodeFromCompileUnit(lUnit, lStreamWriter, lOptions);
    }

    // generate a VB source code file
    VBCodeProvider lVBCodeProvider = new VBCodeProvider();
    using (StreamWriter lStreamWriter = new StreamWriter(lDesktopPath + "HelloWorld.vb", false)) {
        lVBCodeProvider.GenerateCodeFromCompileUnit(lUnit, lStreamWriter, lOptions);
    }
            
} //

example output file HelloWorld.cs:

//------------------------------------------------------------------------------
// 
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.18408
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// 
//------------------------------------------------------------------------------

namespace MyNamespace {
  using System;
  using System.IO;
  
  
  public class MyClass {
    
    public static void Main() {
      Console.WriteLine("hello world !");
    }
    
    private void DoSomething() {
    }
  }
}

example output file HelloWorld.vb:

'------------------------------------------------------------------------------
' 
'     This code was generated by a tool.
'     Runtime Version:4.0.30319.18408
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' 
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports System
Imports System.IO

Namespace MyNamespace
  
  Public Class [MyClass]
    
    Public Shared Sub Main()
      Console.WriteLine("hello world !")
    End Sub
    
    Private Sub DoSomething()
    End Sub
  End Class
End Namespace

In the above example I have added an empty method. We will make it more complex now. The method will be called with parameters from Main() and return a value to a local variable. The local variable will be stored in a field via a property.

public static void Test2() {
    CodeCompileUnit lUnit = new CodeCompileUnit();

    CodeNamespace lNamespace = new CodeNamespace("MyNamespace");
    lNamespace.Imports.Add(new CodeNamespaceImport("System"));
    lUnit.Namespaces.Add(lNamespace);

    CodeTypeDeclaration lClass = new CodeTypeDeclaration("MyClass");
    lClass.IsClass = true;
    lClass.Attributes = MemberAttributes.Public;
    lNamespace.Types.Add(lClass);


    // -----------------------------------------------
    // method DoSomething()
    // -----------------------------------------------

    CodeTypeParameter lClassAsParameter = new CodeTypeParameter(lClass.Name);
    CodeTypeReference lClassReference = new CodeTypeReference(lClassAsParameter);
    CodeParameterDeclarationExpression lClassExpression = new CodeParameterDeclarationExpression(lClassReference, "xClass");
    CodeMemberMethod lDoSomething = new CodeMemberMethod();
    lDoSomething.Attributes = MemberAttributes.Public;
    lDoSomething.Comments.Add(new CodeCommentStatement("This is an example comment for our method DoSomething()."));
    lDoSomething.Name = "DoSomething";
    lDoSomething.ReturnType = new CodeTypeReference(typeof(double));
    lDoSomething.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "xInteger"));
    lDoSomething.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "xString"));
    lDoSomething.Parameters.Add(lClassExpression);
    lDoSomething.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(1.0)));
    lClass.Members.Add(lDoSomething);


    // -----------------------------------------------
    // private field _MyField
    // -----------------------------------------------

    CodeMemberField lField = new CodeMemberField(typeof(long), "_MyField");
    lField.Attributes = MemberAttributes.Private;
    lField.InitExpression = new CodePrimitiveExpression(10);
    lClass.Members.Add(lField);


    // -----------------------------------------------
    // property MyProperty
    // -----------------------------------------------

    CodeMemberProperty lProperty = new CodeMemberProperty();
    lProperty.Name = "MyProperty";
    lProperty.Attributes = MemberAttributes.Public;
    lProperty.Type = new CodeTypeReference(typeof(long));
    lProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(359)));
    lProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), lField.Name), new CodePropertySetValueReferenceExpression()));
    lClass.Members.Add(lProperty);


    // -----------------------------------------------
    // Main()
    // -----------------------------------------------

    CodeEntryPointMethod lMain = new CodeEntryPointMethod();
    lClass.Members.Add(lMain);

    // local double variable d
    CodeVariableReferenceExpression lLocalVariable_d = new CodeVariableReferenceExpression("d");
    CodeVariableDeclarationStatement lLocalVariableStatement_d = new CodeVariableDeclarationStatement(typeof(double), lLocalVariable_d.VariableName);
    lMain.Statements.Add(lLocalVariableStatement_d);

    // local integer variable i
    CodeVariableReferenceExpression lLocalVariable_i = new CodeVariableReferenceExpression("i");
    CodeVariableDeclarationStatement lLocalVariableStatement_i = new CodeVariableDeclarationStatement(typeof(int), lLocalVariable_i.VariableName);
    lLocalVariableStatement_i.InitExpression = new CodePrimitiveExpression(0);
    lMain.Statements.Add(lLocalVariableStatement_i);

    // local class variable MyClass
    CodeVariableReferenceExpression lLocalVariable_Class = new CodeVariableReferenceExpression("lClass");
    CodeVariableDeclarationStatement lLocalVariableStatement_Class = new CodeVariableDeclarationStatement(lClassReference, lLocalVariable_Class.VariableName);
    lLocalVariableStatement_Class.InitExpression = new CodeObjectCreateExpression(lClass.Name, new CodeExpression[] { });
    lMain.Statements.Add(lLocalVariableStatement_Class);

    // DoSomething() method call
    CodeMethodInvokeExpression lDoSomethingExpression = new CodeMethodInvokeExpression(lLocalVariable_Class, lDoSomething.Name, new CodeExpression[] { lLocalVariable_i, new CodePrimitiveExpression("hello"), lLocalVariable_Class});
    CodeAssignStatement lAssignment = new CodeAssignStatement(lLocalVariable_d, lDoSomethingExpression);
    lMain.Statements.Add(lAssignment);
    //lMain.Statements.Add(lDoSomethingExpression); // without assignment

    // cast the "double" result to type "long" and write it to field _MyField via property MyProperty
    CodePropertyReferenceExpression lPropertyExpression = new CodePropertyReferenceExpression(lLocalVariable_Class, lProperty.Name);
    CodeCastExpression lCastExpression = new CodeCastExpression(typeof(long), lLocalVariable_d);
    lAssignment = new CodeAssignStatement(lPropertyExpression, lCastExpression);
    lMain.Statements.Add(lAssignment);

    // -----------------------------------------------
    // create source code
    // -----------------------------------------------

    string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
    CodeGeneratorOptions lOptions = new CodeGeneratorOptions();
    lOptions.IndentString = "  "; // or "\t";
    lOptions.BlankLinesBetweenMembers = true;

    // generate a C# source code file
    CSharpCodeProvider lCSharpCodeProvider = new CSharpCodeProvider();
    using (StreamWriter lStreamWriter = new StreamWriter(lDesktopPath + "MoreComplex.cs", false)) {
        lCSharpCodeProvider.GenerateCodeFromCompileUnit(lUnit, lStreamWriter, lOptions);
    }
} //

example output:

//------------------------------------------------------------------------------
// 
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.18408
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// 
//------------------------------------------------------------------------------

namespace MyNamespace {
  using System;
  
  
  public class MyClass {
    
    private long _MyField = 10;
    
    public virtual long MyProperty {
      get {
        return 359;
      }
      set {
        this._MyField = value;
      }
    }
    
    // This is an example comment for our method DoSomething().
    public virtual double DoSomething(int xInteger, string xString, MyClass xClass) {
      return 1D;
    }
    
    public static void Main() {
      double d;
      int i = 0;
      MyClass lClass = new MyClass();
      d = lClass.DoSomething(i, "hello", lClass);
      lClass.MyProperty = ((long)(d));
    }
  }
}

Is your head burning already? This was quite complex. As usual there are easier ways for lazy people. I do not recommend any shortcuts. It undermines the idea of flexibility.

public static void Test3() {
    CodeCompileUnit lUnit = new CodeCompileUnit();

    CodeNamespace lNamespace = new CodeNamespace("MyNamespace");
    lNamespace.Imports.Add(new CodeNamespaceImport("System"));
    lUnit.Namespaces.Add(lNamespace);

    CodeTypeDeclaration lClass = new CodeTypeDeclaration("MyClass");
    lNamespace.Types.Add(lClass);

    // write Main entry point method
    CodeEntryPointMethod lMain = new CodeEntryPointMethod();
    lMain.Statements.Add(new CodeSnippetExpression("Console.WriteLine(\"hello world !\")"));
    lClass.Members.Add(lMain);

    string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
    CodeGeneratorOptions lOptions = new CodeGeneratorOptions();
    lOptions.IndentString = "  "; // or "\t";
    lOptions.BlankLinesBetweenMembers = true;

    // generate a C# source code file
    CSharpCodeProvider lCSharpCodeProvider = new CSharpCodeProvider();
    using (StreamWriter lStreamWriter = new StreamWriter(lDesktopPath + "HelloWorldForLazyPeople.cs", false)) {
        lCSharpCodeProvider.GenerateCodeFromCompileUnit(lUnit, lStreamWriter, lOptions);
    }
} //

We created source code in the runtime environment. How can we execute it?
The next example compiles a class into an assembly and then executes the Main() method, which is automatically defined as the entry point. This happens in memory. If you want to write the executable code to a disk, then set GenerateInMemory to false and define a proper OutputAssembly path in the compiler parameters (variable lParameters). Use CompileAssemblyFromFile() instead of CompileAssemblyFromSource(). Btw. CompileAssemblyFromDom() would accept CodeCompileUnits (eg. used in Test2) as input data. Therefore you don’t have to generate a source code file to compile your code structure.

private static string[] _MyCode = new string[] { 
    "using System;" +
    "public class MyClass {"  +
    "public static void Main() { Console.WriteLine(\"hello world !\"); }" +
    "}" };


public static void Test4() {
    CSharpCodeProvider lCodeProvider= new CSharpCodeProvider();
    CompilerParameters lParameters = new CompilerParameters();
    //lParameters.ReferencedAssemblies.Add("System.dll");
    lParameters.GenerateInMemory = true;
    lParameters.GenerateExecutable = true;
    lParameters.OutputAssembly = "HelloWorld";

    CompilerResults lCompilerResults = lCodeProvider.CompileAssemblyFromSource(lParameters, _MyCode);

    if (lCompilerResults.Errors.Count > 0) {
        foreach (CompilerError lError in lCompilerResults.Errors) Console.WriteLine(lError.ToString());
    }
    else {
        Console.WriteLine("Compiled succesfully " + lCompilerResults.PathToAssembly);
        Console.WriteLine("Executing code now");
        MethodInfo m = lCompilerResults.CompiledAssembly.EntryPoint;
        m.Invoke(null, new object[] {});

    }
    Console.ReadLine();
} //

example output:
Compiled succesfully
Executing code now
hello world !

Expression Trees

Lambda expressions are using expression trees. The data-structure is like a tree. We are not necessarily talking about code here. Lambda expressions are not source code in the classical sense, because compilers translate expressions into code. In fact you don’t know the result, all you need to do is describing the code in an expression tree. For instance LINQ can interact between your code and a database. It can generate SQL queries. And these have nothing to do with the C# source code.

Let’s use an expression tree to generate another “Hello World” example at runtime. For this we need to implement the System.Linq.Expressions namespace. This is not really a part of reflection. I touch on the topic here to have it mentioned before we continue with Emit, which is far more on spot.

public static void Test5() {
    Expression A = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) }), Expression.Constant("hello world !"));
    Expression B = Expression.Call(null, typeof(Console).GetMethod("ReadLine", new Type[] { }), null);

    Expression[] Expressions = new Expression[] { A, B };
    BlockExpression lBlockExpression = Expression.Block(Expressions);

    Action lAction = Expression.Lambda<Action>(lBlockExpression).Compile();
    lAction();
} //

Let’s build a simple calculator now. Imagine someone enters a mathematical formula in a TextBox and wants to calculate it. I have seen so many solutions on the internet and I can tell you for sure they are all far too long! In the following code you just need to add some “nice features” like pre-parsing to allow more user flexibility. The entire Math library is yours! May the 4th be with you 😉

public static void Test6() {
    string lTextBoxText = "Math.Floor(5.0  * (3.0 + 7.0) / 9.0 * Math.Exp(3.0))";
    string[] lCode = new string[] { 
        "using System;" +
        "public class MyClass {"  +
        "public static double Calc() { return (double)" + lTextBoxText + "; }" +
        "}" };


    CSharpCodeProvider lCodeProvider = new CSharpCodeProvider();
    CompilerParameters lParameters = new CompilerParameters();
    lParameters.GenerateInMemory = true;
    lParameters.GenerateExecutable = false;
    lParameters.OutputAssembly = "CalcAssembly";

    CompilerResults lCompilerResults = lCodeProvider.CompileAssemblyFromSource(lParameters, lCode);

    MethodInfo m = lCompilerResults.CompiledAssembly.GetType("MyClass").GetMethod("Calc");
    double d = (double)m.Invoke(null, null);
    Console.WriteLine("calculation result is " + d);
    Console.WriteLine("cross check result is " + (Math.Floor(5.0 * (3.0 + 7.0) / 9.0 * Math.Exp(3.0))));

    Console.ReadLine();
} //

example output:
calculation result is 111
cross check result is 111

Do you remember the post about extension methods? Unfortunately we cannot write any extension method for static classes like the .Net framework Math class. So I am going to use the string class instead. The calculator looks a little bit nicer then.

public static class MathExtension {
    public static double Formula(this string xFormula) {
        string[] lCode = new string[] { 
            "using System;" +
            "public class MyClass {"  +
            "public static double Calc() { return (double)" + xFormula + "; }" +
            "}" };


        CSharpCodeProvider lCodeProvider = new CSharpCodeProvider();
        CompilerParameters lParameters = new CompilerParameters();
        lParameters.GenerateInMemory = true;
        lParameters.GenerateExecutable = false;
        lParameters.OutputAssembly = "CalcAssembly";

        CompilerResults lCompilerResults = lCodeProvider.CompileAssemblyFromSource(lParameters, lCode);

        MethodInfo m = lCompilerResults.CompiledAssembly.GetType("MyClass").GetMethod("Calc");
        return (double)m.Invoke(null, null);
    } //        

} //

// how to call the extension method
public static void Test7() {
    string lTextBoxText = "Math.Floor(5.0  * (3.0 + 7.0) / 9.0 * Math.Exp(3.0))";
    double d = lTextBoxText.Formula();
    Console.WriteLine(d);
    Console.ReadLine();
} //

Epilogue

There are some stock market trading applications, which allow users to add their own algorithmic trading strategies. Wouldn’t it be great to enter your code in C# and use the .Net framework instead of reinventing the wheel and come up with your own scripting language that will never reach any widely recognized state of the art?

Advertisements