2011-01-17 17 views
7

¿Cómo puedo escribir código C# para compilar y ejecutar código C# generado dinámicamente? ¿Hay ejemplos alrededor?Compilación de código de forma dinámica con C#

Lo que busco es construir dinámicamente una clase C (o clases) y ejecutarlas en tiempo de ejecución. Quiero que la clase generada interactúe con otras clases de C# que no son dinámicas.

He visto ejemplos que generan archivos exe o dll. No estoy buscando eso, solo quiero que compile un código C# en la memoria y luego lo ejecute. Por ejemplo,

Así que aquí es una clase que no es dinámica, que se definirá en mi C# ensamblaje y sólo va a cambiar en tiempo de compilación,

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

Aquí es mi clase que es dinámico. Mi código C# generará esta clase y ejecutarlo,

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

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

Así, el resultado es que al final mi código no dinámico llamaría ProcessValues ​​y sería hacer algunas otras cosas. El objetivo del código dinámico es permitirnos a nosotros o al cliente agregar lógica personalizada al software.

Gracias.

+2

¿Es factible compilar ensamblajes de "complementos" y simplemente cargar esos complementos en tiempo de ejecución en lugar de compilar dinámicamente cosas? –

+0

Correcto, sí, lo he hecho antes para otro proyecto. Eso es posible, pero no es lo que realmente buscamos para este proyecto. Lo consideraré, gracias. – peter

Respuesta

15

dos posibilidades:

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

ACTUALIZACIÓN:

Como petición en la sección de comentarios que aquí está un ejemplo completo que ilustra el uso de Reflection.Emit construir dinámicamente una clase y agregar un stati Método C a la misma:

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

Se puede poner puntos de ruptura dentro de sus métodos no NotDynamicClass y ver cómo consiguen invocados.


ACTUALIZACIÓN 2:

He aquí un ejemplo con CodeDom compilador:

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); 
     } 
    } 
} 
+0

¿Algún buen ejemplo que sugeriría corresponde a lo que quiero hacer arriba? – peter

+0

@peter, por favor vea mi actualización. Agregué un ejemplo que ilustra cómo se puede construir dinámicamente una clase en tiempo de ejecución agregando métodos y al final invocar esos métodos. –

+0

Gracias por eso. Eso es lo que tenía miedo y lo que he visto en otro lado. Está construyendo un método usando GetILGenerator y llamando a il.Emit, etc. ¿No hay una manera de simplemente escribir el código, especificar una cadena que tenga el código y decir 'ejecutar eso' más como mi segundo ejemplo de código anterior.La razón es que queremos escribir estos scripts como 'texto' y almacenarlos en un DB. – peter

Cuestiones relacionadas