Shawn Weisfeld

I find when I talk to myself nobody listens. - Shawn Weisfeld
posts - 352, comments - 144, trackbacks - 34

My Links

News

The views expressed in this blog are mine and mine alone, not that of my employer, Microsoft, or anyone else’s. No warrantee is given for the quality of any material on this site.

Archives

Post Categories

Generate, Compile and Execute Code Dynamically

Like any good developer, when someone comes to my desk and says you cannot do something I have to prove them wrong. This was one of those challenges. He said you cannot “generate, compile and execute code on the fly in .NET”. I said yes you can, he said no you cannot and after five minutes of back and forth I decided to throw together this example.

Step 1: Build the code
Here you have 2 options: code as strings, code from CodeDom. What one you pick depends on what you are doing. In “Programming Microsoft ASP.NET 2.0 Applications Advanced Topics by Dino Esposito” he does a good job of explaining each. “Using a text-writer binds you to a particular language, but it allows you to write code more quickly. The CodeDom API is more abstract and certainly more complex and quirky to write. CodeDom requires a larger memory footprint then a text-writer based solution. In terms of readability, no approach is perfect. CodeDom is hard to read because of its level of abstraction; a text-writer solution has code interspersed with strings and placeholders, and it can be dangerously error-prone to modify and maintain.”

I decided to use the CodeDom technique. So for my example I am going to create a simple static add method that takes in two integers and adds them together and returns the results.

    1         //Step 1: Build the Code

    2         //Create the code unit that we are going to work with

    3         CodeCompileUnit code = new CodeCompileUnit();

    4 

    5         //Give it a namespace

    6         CodeNamespace ns = new CodeNamespace("GeneratedNamespace");

    7         code.Namespaces.Add(ns);

    8 

    9         //Create the class

   10         CodeTypeDeclaration cls = new CodeTypeDeclaration("GeneratedClass");

   11         cls.IsClass = true;

   12         cls.Attributes = MemberAttributes.Public;

   13         ns.Types.Add(cls);

   14 

   15         //Create the method

   16         CodeMemberMethod method = new CodeMemberMethod();

   17         CodeParameterDeclarationExpression val1 = new CodeParameterDeclarationExpression(typeof(int), "value1");

   18         CodeParameterDeclarationExpression val2 = new CodeParameterDeclarationExpression(typeof(int), "value2");

   19         method.Attributes = MemberAttributes.Public | MemberAttributes.Static;

   20         method.Name = "Add";

   21         method.Parameters.Add(val1);

   22         method.Parameters.Add(val2);

   23         method.ReturnType = new CodeTypeReference(typeof(int));

   24 

   25         method.Statements.Add(

   26             new CodeMethodReturnStatement(

   27             new CodeBinaryOperatorExpression(

   28             new CodeArgumentReferenceExpression("value1"),

   29             CodeBinaryOperatorType.Add,

   30             new CodeArgumentReferenceExpression("value2"))));

   31         cls.Members.Add(method);

Now that you have the code generated you might want to take a look at it. You can convert it to a string by doing the following:

    1         //Output the generated code to the page so we can see it

    2         StringWriter sw = new StringWriter(new StringBuilder());

    3         CodeDomProvider cdp = new CSharpCodeProvider();

    4         cdp.GenerateCodeFromCompileUnit(code, sw, null);

    5         Response.Write(sw.ToString().Replace("\n", "<br \\>"));

 
The generated code will look something like this. . .

    1 //------------------------------------------------------------------------------

    2 //

    3 // This code was generated by a tool.

    4 // Runtime Version:2.0.50727.1433

    5 //

    6 // Changes to this file may cause incorrect behavior and will be lost if

    7 // the code is regenerated.

    8 //

    9 //------------------------------------------------------------------------------

   10 

   11 namespace GeneratedNamespace

   12 {

   13     public class GeneratedClass

   14     {

   15         public static int Add(int value1, int value2)

   16         {

   17             return (value1 + value2);

   18         }

   19     }

   20 }

Step 2: Compile it
Now that you have your code (in a string or in the CodeDom) you can request that .NET compile it for you. First you need to determine what language you want to compile it using. I am going to use C# but you can easily change it out for VB, etc.

    1         //Step 2: Compile the Code

    2         CompilerParameters cp = new CompilerParameters();

    3         cp.GenerateExecutable = false;

    4         cp.GenerateInMemory = true;

    5         cp.ReferencedAssemblies.Add("System.dll");

    6         CompilerResults results = cdp.CompileAssemblyFromDom(cp, code);

    7         Assembly assembly = results.CompiledAssembly;

Step 3: Run it
Now it is just a matter of using reflection to take the assembly that we generated and invoke its method.

    1         //Step 3: Run it using reflection

    2         Type t = assembly.GetType("GeneratedNamespace.GeneratedClass");

    3         object[] parms = { 5, 5 };

    4         Response.Write(t.GetMethod("Add").Invoke(null, parms));

So while this was an academic exercise to prove that it can be done, it does have real implications for real applications, but I will leave that to your imagination.

Ideas borrowed from:
Dino Esposito - http://weblogs.asp.net/despos/
CodeDom Calculator - http://channel9.msdn.com/ShowPost.aspx?PostID=360076
GotDotNet - http://samples.gotdotnet.com/quickstart/util/srcview.aspx?path=/quickstart/howto/samples/CompMod/CodeDom/ListBuilder/listbuilder.src&file=CS\listbuilder.cs&font=3

Print | posted on Wednesday, December 12, 2007 3:17 PM | Filed Under [ .NET C# ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 1 and 2 and type the answer here:

Powered by: