Occasionally we might come across a problem which lends itself well to the idea of C# code being compiled at runtime, maybe we’ve created some script like plug-in or in my case I wanted to generate sample XML data from xsd.exe generated classes at runtime.
We can use the CSharpCodeProvider to do exactly this, it can compile some code, create an assembly (in my case in-memory) and then allow us to instantiate the code within that assembly. Let’s jump straight into some code and then we’ll look at how the code works
var param = new CompilerParameters { GenerateExecutable = false, IncludeDebugInformation = false, GenerateInMemory = true }; param.ReferencedAssemblies.Add("System.dll"); param.ReferencedAssemblies.Add("System.Xml.dll"); param.ReferencedAssemblies.Add("System.Data.dll"); param.ReferencedAssemblies.Add("System.Core.dll"); param.ReferencedAssemblies.Add("System.Xml.Linq.dll"); var codeProvider = new CSharpCodeProvider(); var results = codeProvider.CompileAssemblyFromFile(param, filename); if (results.Errors.HasErrors) { foreach (var error in results.Errors) { Console.WriteLine(error); } } else { object o = results. CompiledAssembly. CreateInstance(typeName); }
In the code above, we’re assuming that the source code we want to compile exists in a seperate C# source code file stored in the variable filename.
Firstly we create the CompilerParameters. I don’t want an executable to be generated and debug information will be of little use to me. I’m going to create the resultant assembly in memory as we’re not intending to write this to disc.
Next up we need to tell the compiler what assemblies will be required, ofcourse this might be best supplied in some alternate way, such as the script itself could be parsed or we might have another file with the assemblies listed, but for my purposes I’ll just list the “standard” assemblies.
We create a CSharpCodeProvider (as we’re working in C#) and passing the compiler parameters and the source code filename we get the code provider to compile the code. If any errors occur we simply list them (in this example, to the console).
Assuming all compiles we use the CompiledAssembly and create an instance of the type we’re interested in. In my example I supplied the typeName (i.e. class name) in the command line arguments to the application which uses obviously allows this code to be a little more flexible.
Once we’ve got the instance of the type we can obviously start interacting with it.