3 décembre 2008

Exécuter une chaine de caractère avec CodeDom et la réflexion

Grâce à l'API CodeDom, il est assez facile de générer des assemblies dynamiquement. Le framework .Net permet d'exécuter un assembly grâce à la réflexion. A partir de ces 2 constats, il devient possible d'exécuter une chaine de caractère contenant du code, un peu comme on le ferait dans un langage de script.

Dans un langage statique comme le C#, le code doit d'abord être compilé, ce que CodeDom fait très bien. Il ne reste plus qu'à utiliser la réflexion pour exécuter le code compilé. Voici l'exemple d'une fonction montrant cela :

   1:  using System;
   2:  using System.CodeDom;
   3:  using System.CodeDom.Compiler;
   4:  using System.Reflection;
   5:   
   6:  namespace GenererMethode
   7:  {
   8:      public class Generateur
   9:      {
  10:          public static void Executer(string[] references, string[] usings, string strCode)
  11:          {
  12:              CompilerParameters compilerParameters = new CompilerParameters();
  13:              compilerParameters.GenerateExecutable = false;
  14:              compilerParameters.GenerateInMemory = true;
  15:              compilerParameters.IncludeDebugInformation = true;
  16:   
  17:              foreach (string reference in references)
  18:              {
  19:                  compilerParameters.ReferencedAssemblies.Add(reference);
  20:              }
  21:   
  22:              CodeCompileUnit code = new CodeCompileUnit();
  23:              code.UserData.Add("AllowLateBound", false);
  24:              code.UserData.Add("RequireVariableDeclaration", true);
  25:   
  26:              CodeNamespace nameSpace = new CodeNamespace("GenererMethode");
  27:              foreach (string _using in usings)
  28:              {
  29:                  nameSpace.Imports.Add(new CodeNamespaceImport(_using));
  30:              }
  31:              code.Namespaces.Add(nameSpace);
  32:   
  33:              CodeTypeDeclaration classObject = new CodeTypeDeclaration("Test");
  34:              nameSpace.Types.Add(classObject);
  35:              classObject.TypeAttributes = TypeAttributes.Public;
  36:   
  37:              CodeMemberMethod method = new CodeMemberMethod();
  38:              method.Attributes = MemberAttributes.Public  MemberAttributes.Static;
  39:              method.Name = "HelloWorld";
  40:              CodeSnippetExpression csu = new CodeSnippetExpression(strCode);
  41:              method.Statements.Add(csu);
  42:              classObject.Members.Add(method);
  43:   
  44:              CodeGenerator.ValidateIdentifiers(code);
  45:   
  46:              CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("C#");
  47:   
  48:              var result = codeProvider.CompileAssemblyFromDom(compilerParameters, code);
  49:   
  50:              Assembly assembly = result.CompiledAssembly;
  51:              Type type = assembly.GetType("GenererMethode.Test");
  52:              type.InvokeMember("HelloWorld", BindingFlags.InvokeMethod, null, null, null);
  53:          }
  54:      }
  55:  }

La méthode "Executer" crée une classe quelconque et créé aussi une méthode statique contenant la chaine de caractère passée en paramètre. Pour que le code de la chaine de caractères puisse s'exécuter, la méthode prend en paramètre les dll contenant les assemblies et les usings nécessaires. Finalement, la méthode générée est exécutée via la réflexion.


   1:  Generateur.Executer(new string[] { "System.dll" },
   2:                                  new string[] { "System" },
   3:                                  "Console.WriteLine(\"Hello World\")");

Aucun commentaire: