Programmation dynamique
Introduction
Courte présentation (une petite approche) des techniques qui permettent la programmation dynamique : réflexion, génériques, arborescence d'expression, LINQ, création d'IL, compilation dynamique, DLR.
Bonne lecture...
La programmation dynamique
Par réflexion
Pour rappel : vous pouvez retourner le type (
System.Type) d'un objet avec sa méthode
GetType() (héritage de
object) et d'un type avec le mot clef
typeof. Partant de là, il est possible de connaître toutes les composantes de l'objet et de son type et d'utiliser ses méthodes -hors discussion sur les permissions-. Il existe d'autres mots clefs (is, as) et classes pour compléter l'utilisation. On peut ainsi `parcourir' et utiliser tout son module...
Exemple : utilisation d'une méthode connue d'une classe 'inconnue' contenant cette méthode
public void Add<T1, T2>(T1 liste, T2 item) {
liste.GetType().GetMethod("Add").Invoke(liste, new object[] { item });
}
Avec le type dynamic (C#4)
C'est un nouveau type du prochain Framework 4 permettant de passer la compilation sans vérification (donc pas d'IntelliSense). Le type réel sera défini et vérifié dynamiquement par réflexion
Ex :
dynamic objet = ..un objet, par exemple un List<string>..; objet.Add("string1");
De même, avec
ScriptObject. Ex :
ScriptObject liste = ..un objet, par exemple un List<string>..; liste.Invoke("Add", "string1");
Par utilisation d'un DynamicProxy
Il simule dynamiquement un objet (pouvant être distant) en interceptant les appels sur cet objet. Pour l'utiliser il faut dériver de la classe
RealProxy (classe abstract de
System.Runtime.Remoting.Proxies) et implémenter la méthode
Invoke qui sera appelée pour chaque appel sur l'objet derrière le proxy.
Avec les génériques
Les délégués génériques :
- Func<..TypesParamètres.., TypeDeRetour> eq. à delegate TypeDeRetour Func(..TypesParamètres..);
- Action<..TypesParamètres..> eq. à delegate void Func(..TypesParamètres..);
Les classes génériques, ex :
public void CréationListGen<T>(T objet) {
var typeListeGen = typeof(List<>);
// Le CLR va définir dynamiquement le type générique (ex string). Equivaut ici à List<T>
var typeListeDeStrings = typeListeGen.MakeGenericType(typeof(T));
// Création d'une liste, juste pour l'exemple
var listeDeStrings = typeListeDeStrings.GetConstructor(Type.EmptyTypes).Invoke(null);
// Ajout d'un élément, juste pour l'exemple
listeDeStrings.GetType().GetMethod("Add").Invoke(listeDeStrings, new object[] { objet });
}
Les arbres d'expressions
La classe Expression contient des méthodes de fabrique statiques qui créent des noeuds d'arborescence d'expression. L'espace de noms System.Linq.Expressions fournit une API pour générer des arborescences d'expression manuellement. On peut également fournir un délégué qui sera -si possible- décomposé en arbre. Cet arbre peut être parcouru, modifié et compilé dynamiquement. Ex :
// Expression lambda décomposée sous forme d'arbre.
Expression<Func<double, double>> expr = x => Math.Pow(x, 2) + 3 * x + 5;
// L'arbre est compilé en code qui pourra être exécuté par appel de delExpr.
Func<double, double> delExpr = expr.Compile();
double val = delExpr(5.0);
Avec LINQ
En effet les requêtes (notamment avec Linq to SQL) sont repoussées et exécutées uniquement au moment de leur utilisation. Pour ce faire Linq utilise abondamment les génériques, les expressions lambda et les arborescences d'expression.
Note : on peut - pour améliorer les performances - compiler ses requêtes. Ex :
System.Data.Linq.CompiledQuery.Compile((source, type de paramètres optionnels) => requête linq);
Par création et exécution dynamique de code par construction en IL
Dans
System.Reflection.Emit plusieurs classes (
DynamicMethod, etc) permettent de construire du code IL (
Intermediate Language) qui peut être interprété dynamiquement.
Ex :
// Type des paramètres
Type[] helloArgs = { typeof(string), typeof(int) };
// Méthode crée dynamiquement
DynamicMethod hello = new DynamicMethod("Hello", // son nom
typeof(int), // type du retour
helloArgs, // type des paramètres
typeof(string).Module);
// On prépare l'injection du code : sortie sur la Console...
Type[] writeStringArgs = { typeof(string) };
MethodInfo writeString = typeof(Console).GetMethod("WriteLine", writeStringArgs);
ILGenerator il = hello.GetILGenerator(256); // Injection du code...
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, writeString, null);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ret);
// Infos sur les paramètres ; pour le debugging
ParameterBuilder parameter1 = hello.DefineParameter(1, ParameterAttributes.In, "message");
ParameterBuilder parameter2 = hello.DefineParameter(2, ParameterAttributes.In, "valueToReturn");
// Un delegate pour exécuter la méthode dynamique.
HelloDelegate hi = (HelloDelegate)hello.CreateDelegate(typeof(HelloDelegate)); // avec delegate int HelloDelegate(string msg, int ret);
int retval = hi("rnHello, World!", 42);
Par envoi d'instruction sous forme de string au compilateur
Les classes dans l'espace
Microsoft.CSharp (et
System.CodeDom.Compiler) permettent de compiler (en mémoire ou dans un fichier) et le cas échéant d'exécuter du code contenu dans un string.
Ex :
CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeCompiler compileur = provider.CreateCompiler();
CompilerResults cr = provider.CompileAssemblyFromSource(new CompilerParameters(), @"using System; public class Eval{public static double MultPar2(double x) { return x * 2; } }");// Le code.
if (cr.Errors.Count > 0) throw new Exception("Erreur sur le code dynamique.");
var ret = cr.CompiledAssembly.GetType("Eval").GetMethod("MultPar2").Invoke(null, new object[] { 50 });// Retourne 100.
Par utilisation des langages dynamiques (avec la DLR)
La Dynamic Language Runtime est une surcouche du .NET Framework 3.5 permettant de faciliter la création de langages dynamiques pour .NET. Ce langage peut être un langage intégré à une application ou plus largement l'implémentation d'un nouveau langage pour .NET à l'image d'IronPython ou de IronRuby. Son utilisation est facilitée avec Silverlight notamment avec le Javascript et/ou Python.
Ressources
Source en vidéo :
http://www.microsoft.com/france/vision/mstechdays09/Webcast.aspx?eID=490a4112-6a60-403d-9e86-9ada349529b1