matthew ephraim

Treating C# Like A Scripting Language

Creating code on the fly

One thing that I like about scripting languages is their ability to dynamically modify code during runtime. Ruby and JavaScript, for example, both give you the ability to load in code directly from a string and execute it as part of your program. While that sort of thing can be dangerous, it also gives you access to some really fun metaprogramming techniques.

While working on a simple DSL for one my ASP.NET sites, I started to wonder if C# had some similar functionality that I could take advantage of. In particular, I wanted to load in C# code from a file and execute the code inside of it. What I found was that C# does indeed have the ability to accomplish this task, albeit in sort of an ugly way.

Using C# to compile C#

Most scripting languages give you a function that allows you directly evaluate a block or raw string of code as soon as it’s encountered. Because C# is a compiled language, it’s a little bit more complicated. The C# code needs to be compiled into an assembly before it can be used. And then classes from the compiled code can be instantiated directly from the assembly.

C# code can be compiled on the fly with an instance of the CSharpCodeProvider class (there’s a similar class for VB.Net as well). Additionally, you can create an instance of the CompilerParameters class, which contains a collection of parameters that will be used when compiling your code. In the example below, I’m creating a new C# compiler and a set of parameters that will tell it not to create an assembly file, but to instead compile the new assembly in memory. I also tell the compiler to include System.dll as a reference assembly.

C#
// Create a new instance of the C# compiler
var compiler = new CSharpCodeProvider();

// Create some parameters for the compiler
var parms    = new System.CodeDom.Compiler.CompilerParameters
{
    GenerateExecutable      = false,
    GenerateInMemory        = true
};
parms.ReferencedAssemblies.Add("System.dll");

Once a C# compiler has been created, you can use it to compile raw source into an assembly. CSharpCodeProvider allows you to compile code from a variety of sources. In the example below, I’m using the CompileAssemblyFromSource method to compile my code directly from an array of strings. CompileAssemblyFromSource will look at the code provided and return an instance of the CompilerResults class.

C#
// Try to compile the string into an assembly
var results = compiler.CompileAssemblyFromSource(parms, new string[]
{@" using System;

    class MyClass
    {
        public void Message(string message)
        {
            Console.Write(message);
        }               
    }"});

One thing to note is that the compilation method will complete regardless of whether or not the code has compiled successfully. To make sure you code has compiled, you need to check the Errors collection that is part of the CompilerResults instance returned by CompileAssemblyFromSource. If there were no errors, the code was compiled successfully and you can begin using the assembly.

Using the compiled code

Once your code is compiled into an assembly, you can use that assembly to create instances of classes from your source code and use reflection to invoke methods and get and set properties of those classes. In the example below, I’m creating an instance of MyClass and storing it as an object. I’m then using reflection to invoke the Message method on the class.

C#
// If there weren't any errors get an instance of "MyClass" and invoke
// the "Message" method on it
if (results.Errors.Count == 0)
{
    var myClass = results.CompiledAssembly.CreateInstance("MyClass");
    myClass.GetType().
            GetMethod("Message").
            Invoke(myClass, new []{ "Hello World!" });
}

It’s not exactly pretty, but it gets the job done. Scripting languages make it much easier to accomplish this sort of task, but it’s still nice to see that it can be done in C#.