Динамическая компиляция кода с использованием C # - PullRequest
7 голосов
/ 18 января 2011

Как я могу написать код C # для компиляции и запуска динамически сгенерированного кода C #. Есть ли примеры вокруг?

Я хочу динамически создать класс (или классы) C # и запускать их во время выполнения. Я хочу, чтобы сгенерированный класс взаимодействовал с другими классами C #, которые не являются динамическими.

Я видел примеры, которые генерируют файлы exe или dll. Я не после этого, я просто хочу, чтобы он скомпилировал код C # в памяти и затем запустил его. Например,

Так вот класс, который не является динамическим, он будет определен в моей сборке C # и будет меняться только во время компиляции,

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
    }
}

Вот мой класс, который динамичен. Мой код C # сгенерирует этот класс и запустит его,

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

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

Таким образом, в результате мой не динамический код вызвал бы ProcessValues, и это сделало бы некоторые другие вещи. Смысл динамического кода заключается в том, чтобы позволить нам или клиенту добавить собственную логику в программное обеспечение.

Спасибо.

Ответы [ 2 ]

15 голосов
/ 18 января 2011

Две возможности:

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

UPDATE:

В качестве запроса в разделе комментариев приведен полный пример, иллюстрирующий использование Reflection.Emit для динамического построения класса и добавления к нему статического метода:

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]);
    }
}

Вы можете поместить точки останова в ваши не NotDynamicClass методы и посмотреть, как они вызываются.


ОБНОВЛЕНИЕ 2:

Вот пример с компилятором CodeDom:

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);
        }
    }
}
2 голосов
/ 18 января 2011

Mono имеет C # компилятор в качестве службы , который можно использовать в приложениях .NET в Windows (наряду с Linux, конечно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...