2011-01-17 15 views
7

Come posso scrivere codice C# per compilare ed eseguire codice C# generato in modo dinamico. Ci sono esempi in giro?Compilazione dinamica del codice utilizzando C#

Quello che devo fare è creare dinamicamente una classe (o classi) C# ed eseguirli in fase di runtime. Voglio che la classe generata interagisca con altre classi C# non dinamiche.

Ho visto esempi che generano file exe o dll. Non lo sono poi, voglio solo che compili un codice C# in memoria e poi lo esegua. Per esempio,

ecco una classe che non è dinamica, sarà definito nel mio montaggio C# e cambierà solo in fase di compilazione,

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

Qui è la mia classe che è dinamica. Il mio codice C# genera questa classe ed eseguirlo,

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

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

Così il risultato è che alla fine il mio codice non dinamica chiamerebbe processValues ​​e sarebbe fare qualche altra roba. Il punto del codice dinamico è di consentire a noi o al cliente di aggiungere una logica personalizzata al software.

Grazie.

+2

È possibile compilare gli assembly "plug-in" e caricare questi plug-in in fase di runtime anziché compilare dinamicamente le cose? –

+0

Giusto, sì l'ho già fatto per un altro progetto. Questo è possibile, ma non proprio quello che cerchiamo per questo progetto. Lo prenderò in considerazione, grazie. – peter

risposta

15

due possibilità:

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

UPDATE:

Come richiesta nella sezione commenti qui è un esempio completo che illustra l'utilizzo di Reflection.Emit per creare dinamicamente una classe e aggiungere uno stato Metodo C ad esso:

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

Si potrebbe mettere punti di interruzione all'interno del vostro non NotDynamicClass metodi e vedere come vengono invocati.


UPDATE 2:

Ecco un esempio con CodeDom compilatore:

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

Qualche buon esempio che suggeriresti corrispondente a ciò che voglio fare sopra? – peter

+0

@peter, vedere il mio aggiornamento. Ho aggiunto un esempio che illustra come è possibile creare dinamicamente una classe in fase di esecuzione aggiungendo dei metodi e alla fine richiamare tali metodi. –

+0

Grazie per quello. Questo è ciò di cui avevo paura e ciò che ho visto altrove. Stai costruendo un metodo usando GetILGenerator e chiamando il.Emit ecc. Non c'è un modo per scrivere semplicemente il codice, specificare una stringa che contiene il codice e dire "esegui quello" più come il mio secondo esempio di codice sopra.Il motivo è che vogliamo scrivere questi script come "testo" e memorizzarli in un DB. – peter

Problemi correlati