There are some very interesting features in .NET with little in the way of clear documentation. One of these is the CodeProvider: a way to use .NET compilers from within .NET. While this may seem a bit odd, it allows for some very advanced reflection, and things like scripting to be implemented without using any weird hacks (though the concept of including an entire set of compilers in the redistributable runtime is a bit… questionable).
Here is a hack I came up with, which uses “assembly recursion”. It’s actually quite efficient, since assemblies are cached, even if generated by CodeProvider (which saves dll’s of the compiled assemblies in your temp folder with a hash for a name).
I have two projects, once I call DrosteSharp, the other I call RecurseLib. DrosteSharp is a console application, RecurseLib is a class library referenced by DrosteSharp. To get this to work, under the properties for Recurse.cs, I set “Copy To Output Directory” to “Copy If Newer”.
The effect of this program is that it compiles a copy of itself, then accesses an object in the copy of itself through a shared interface, then calls a virtual function through the interface which starts the cycle again in the newly generated assembly.
Recurse.cs (in the DrosteSharp project):
using System;
using System.IO;
using System.Collections.Generic;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
namespace DrosteSharp
{
public class Recurse : RecurseLib.IRecurse
{
public override void Do()
{
Console.WriteLine("Do() called");
string code;
using (StreamReader reader = new StreamReader("Recurse.cs"))
{
code = reader.ReadToEnd();
}
CSharpCodeProvider compiler = new CSharpCodeProvider();
CompilerParameters compilerParams = new CompilerParameters(
new string[] { "System.dll", "RecurseLib.dll" });
CompilerResults compilerResults = compiler.CompileAssemblyFromSource(
compilerParams, new string[] { code });
object inst = compilerResults.CompiledAssembly.CreateInstance("DrosteSharp.Recurse");
((RecurseLib.IRecurse)inst).Do();
}
}
public class Program
{
static void Main(string[] args)
{
Recurse recurse = new Recurse();
recurse.Do();
}
}
}
IRecurse.cs (in the RecurseLib project):
namespace RecurseLib
{
public abstract class IRecurse
{
public abstract void Do();
}
}
Tags: .net, c#, codeprovider, compiler, recursion