Alternate Title: An Incredibly Simple Introduction to C# Metaprogramming

The eval method is a staple of scripting languages.  It takes a string and executes it as code.  This is useful when, e.g., you need to modify the value of one of several text fields on a page.  The name of the text field can be dynamically passed into the eval argument.

So C#, being a compiled language, doesn’t have an eval method. What it does have, however, is great support for programmatic compilation of code. This can be used to hack together something that’s kind of like an eval. Take, for instance, the following method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        public static void FauxEval(string codeToEval)
        {
            //Create compiler objects
            CSharpCodeProvider Compiler = new CSharpCodeProvider();
            CompilerParameters Params = new CompilerParameters();
            CompilerResults Results;
 
            Params.GenerateExecutable = true;
            Params.OutputAssembly = "eval.exe";
 
            //We could check this Results object, but this is naught but an example
            Results = Compiler.CompileAssemblyFromSource(Params, codeToEval);
 
            //Execute the eval-ed code
            Process evalledExecutable = new Process();
            evalledExecutable.StartInfo.FileName = "eval.exe";
            evalledExecutable.Start();
            evalledExecutable.WaitForExit();
        }

This method takes the C# source of an executable as its argument. It compiles the source and executes it. Now this is kind of like an eval. It’s more structured than that found in a scripting language, since it can only eval code that compiles into a valid C# program. The heavy lifting is done by the CSharpCodeProvider class, which handles the compilation of the source. This class also contains a lot of handy methods to generate code from various CodeDOM objects, making it your one-stop shop for C# metaprogramming. (N.B. There’s a comparable class for Visual Basic called VBCodeProvider. This is exceptionally handy if, for whatever reason, you want to use C# to emit Visual Basic code.)

So the above FauxEval method can be used simply by assembling a string that will compile into an executable. The strength of eval-like methods, of course, is that this string can be constructed dynamically at run time. As a completely trivial example, here’s a program that uses the above eval to generate a Hello World-like program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
        static void Main(string[] args)
        {
            string programMessage = "Hello, world!";
            if (args.Length > 0)
            {
                programMessage = args[0];
            }
            string programCode = "";
            programCode += "using System;";
            programCode += "using System.Text;";
            programCode += "namespace GeneratedCode";
            programCode += "{";
            programCode += "class GeneratedProgram";
            programCode += "{";
            programCode += "static void Main(string[] args)";
            programCode += "{";
            programCode += "Console.WriteLine(\"" + programMessage + "\");";
            programCode += "Console.WriteLine(\"Press any key to continue...\");";
            programCode += "Console.ReadKey(true);";
            programCode += "}";
            programCode += "}";
            programCode += "}";
 
            FauxEval(programCode);
        }

The generated program will simply print the first command line argument to the console. If no argument is provided, the executable is built to be a default “Hello, World”.

The results:


(Click to embiggen)

So that’s a super-simple C# eval. This provides the ability to execute code that’s been dynamically created at run time, provided the code can be compiled into an executable. There’s a lot of room to expand on the method and make it more flexible and general, but there really is only so much you can do with this strategy of code evaluation, since the code always has to be able to be compiled into an assembly. Still, it’s a damn sight better than nothing.

Update 2010.3.30: I removed some random debugging code from the Eval method above. It was a classic case of YAGNI. I thought I might want to dump eval string contents to a file. I ended up not needing to, but then forgot to pull the debugging code.