7

How can I wrote C# code to compile and run dynamically generated C# code. Are there examples around?

What I am after is to dynamically build up a C# class (or classes) and run them at runtime. I want the generated class to interact with other C# classes that are not dynamic.

I have seen examples that generate exe or dll files. I am not after that, I just want it to compile some C# code in memory and then run it. For instance,

So here is a class which is not dynamic, it will be defined in my C# assembly and will only change at compile time,

public class NotDynamicClass
{
    private readonly List<string> values = new List<string>();

    public void AddValue(string value)
    {
        values.Add(value);
    }

    public void ProcessValues()
    {
        // do some other stuff with values
    }
}

Here is my class that is dynamic. My C# code will generate this class and run it,

public class DynamicClass
{
    public static void Main()
    {
        NotDynamicClass class = new NotDynamicClass();

        class.AddValue("One");
        class.AddValue("two");
    }
}

So the result is that at the end my non dynamic code would call ProcessValues and it would do some other stuff. The point of the dynamic code is to allow us or the client to add custom logic to the software.

Thanks.

peter
  • 13,009
  • 22
  • 82
  • 142
  • 2
    Is it feasible to compile "plugin" assemblies and just load those plugins at runtime instead of dynamically compiling stuff? – Anon. Jan 17 '11 at 21:57
  • Right, yes I have done that before for another project. That is possible, but not really what we are after for this project. I will consider it, thanks. – peter Jan 17 '11 at 21:58

2 Answers2

16

Two possibilities:

  1. Reflection.Emit
  2. System.CodeDom.Compiler

UPDATE:

As request in the comments section here's a full example illustrating the usage of Reflection.Emit to dynamically build a class and add a static method to it:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

public class NotDynamicClass
{
    private readonly List<string> values = new List<string>();

    public void AddValue(string value)
    {
        values.Add(value);
    }

    public void ProcessValues()
    {
        foreach (var item in values)
        {
            Console.WriteLine(item);
        }
    }
}

class Program
{
    public static void Main()
    {
        var assemblyName = new AssemblyName("DynamicAssemblyDemo");
        var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, false);
        var typeBuilder = moduleBuilder.DefineType("DynamicClass", TypeAttributes.Public);

        var methodBuilder = typeBuilder.DefineMethod(
            "Main",
            MethodAttributes.Public | MethodAttributes.Static,
            null,
            new Type[0]
        );

        var il = methodBuilder.GetILGenerator();
        var ctor = typeof(NotDynamicClass).GetConstructor(new Type[0]);
        var addValueMi = typeof(NotDynamicClass).GetMethod("AddValue");
        il.Emit(OpCodes.Newobj, ctor);
        il.Emit(OpCodes.Stloc_0);
        il.DeclareLocal(typeof(NotDynamicClass));
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ldstr, "One");
        il.Emit(OpCodes.Callvirt, addValueMi);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ldstr, "Two");
        il.Emit(OpCodes.Callvirt, addValueMi);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Callvirt, typeof(NotDynamicClass).GetMethod("ProcessValues"));
        il.Emit(OpCodes.Ret);
        var t = typeBuilder.CreateType();
        var mi = t.GetMethod("Main");
        mi.Invoke(null, new object[0]);
    }
}

You could put breakpoints inside your not NotDynamicClass methods and see how they get invoked.


UPDATE 2:

Here's an example with CodeDom compiler:

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using Microsoft.CSharp;

public class NotDynamicClass
{
    private readonly List<string> values = new List<string>();

    public void AddValue(string value)
    {
        values.Add(value);
    }

    public void ProcessValues()
    {
        foreach (var item in values)
        {
            Console.WriteLine(item);
        }
    }
}

class Program
{
    public static void Main()
    {
        var provider = CSharpCodeProvider.CreateProvider("c#");
        var options = new CompilerParameters();
        var assemblyContainingNotDynamicClass = Path.GetFileName(Assembly.GetExecutingAssembly().Location);
        options.ReferencedAssemblies.Add(assemblyContainingNotDynamicClass);
        var results = provider.CompileAssemblyFromSource(options, new[] 
        { 
@"public class DynamicClass
{
    public static void Main()
    {
        NotDynamicClass @class = new NotDynamicClass();
        @class.AddValue(""One"");
        @class.AddValue(""Two"");
        @class.ProcessValues();
    }
}"
        });
        if (results.Errors.Count > 0)
        {
            foreach (var error in results.Errors)
            {
                Console.WriteLine(error);
            }
        }
        else
        {
            var t = results.CompiledAssembly.GetType("DynamicClass");
            t.GetMethod("Main").Invoke(null, null);
        }
    }
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Any good examples you would suggest corresponding to what I want to do above? – peter Jan 17 '11 at 22:18
  • @peter, please see my update. I've added an example which illustrates how you could dynamically build a class at runtime by adding methods to it and at the end invoke those methods. – Darin Dimitrov Jan 17 '11 at 23:13
  • Thanks for that. That is what I was afraid of and what I have seen elsewhere. You are building a method using GetILGenerator and calling il.Emit etc. Isn't there a way to just write the code, specify a string which has the code in it and say 'run that' more like my second code example above. The reason is that we want to write these scripts as 'text' and store them in a DB. – peter Jan 18 '11 at 00:32
  • @peter, please see my second updated. Added example with CodeDom compiler. – Darin Dimitrov Jan 18 '11 at 07:36
  • Thanks, perfect. That looks just excellent. Can't fault that at all!! – peter Jan 18 '11 at 20:48
  • In addition I have that working in my project now. I was able to replace an old third party script engine with only about 1 hour of dev work. – peter Jan 19 '11 at 00:22
  • @Darin Dimitrov: Good Example, will i be able to change the generated assembly version its always 0.0.0.0. And if i replace this dll with an already existing dll in a deployed website , will that work ?. By the way i use CodeDom compiler way. – Novice Mar 29 '11 at 13:03
2

Mono has a C# compiler as a service that's usable in .NET applications on Windows (along with Linux, of course).

adamjford
  • 7,478
  • 6
  • 29
  • 41