En C #, l’API d’expression est-elle meilleure que Reflection

Aujourd’hui, j’explore les API C # Expression. Donc, je pourrais utiliser une aide pour comprendre comment cela fonctionne, y compris la différence entre expression et reflection. Je veux aussi comprendre si les expressions ne sont que du sucre syntaxique, ou sont-elles vraiment meilleures que celles de la reflection?

De bons exemples ainsi que des liens vers de bons articles seraient appréciés. 🙂

En ce qui concerne l’appel d’une méthode:

  • L’appel direct ne peut pas être battu en vitesse.
  • L’utilisation d’API Expression est globalement similaire à celle de Reflection.Emit ou Delegate.CreateDelegate vitesse (de petites différences peuvent être mesurées, car l’optimisation de la vitesse sans mesures ni objectives est inutile).

    Ils génèrent tous des IL et le framework les comstackra en un code natif à un moment donné. Mais vous payez toujours le coût d’un niveau d’indirection pour appeler le délégué et un appel de méthode dans votre délégué.

    L’API d’expression est plus limitée, mais son ordre de grandeur est plus simple à utiliser, car vous n’avez pas besoin d’apprendre l’IL.

  • Le Dynamic Language Runtime utilisé directement ou via le mot-clé dynamic de C # 4 ajoute un peu de temps mais rest proche de l’émission de code car il met en cache la plupart des contrôles liés aux types de parameters, à l’access et au rest.

    Lorsqu’il est utilisé via le mot-clé dynamic il obtient également la syntaxe la plus précise car elle ressemble à un appel de méthode normal. Mais si vous utilisez la dynamic, vous êtes limité aux appels de méthode alors que la bibliothèque peut faire beaucoup plus (voir IronPython )

  • System.Reflection.MethodInfo.Invoke est lent: en plus des autres méthodes nécessaires pour vérifier les droits d’access, vérifiez le nombre d’arguments, le type, … contre MethodInfo chaque fois que vous appelez la méthode.

Jon Skeet obtient également de bons points dans cette réponse: Delegate.CreateDelegate vs DynamicMethod vs Expression


Certains échantillons, la même chose faite de différentes manières.

Le nombre et la complexité des lignes vous permettraient déjà de savoir quelles solutions sont faciles à entretenir et qu’il convient d’éviter du sharepoint vue de la maintenance à long terme.

La plupart des échantillons sont inutiles mais ils démontrent les classes / syntaxes de base de génération de code de C #, pour plus d’informations, il y a toujours le MSDN

PS: Dump est une méthode LINQPad .

 public class Foo { public ssortingng Bar(int value) { return value.ToSsortingng(); } } void Main() { object foo = new Foo(); // We have an instance of something and want to call a method with this signature on it : // public ssortingng Bar(int value); Console.WriteLine("Cast and Direct method call"); { var result = ((Foo)foo).Bar(42); result.Dump(); } Console.WriteLine("Create a lambda closing on the local scope."); { // Useless but i'll do it at the end by manual il generation Func func = i => ((Foo)foo).Bar(i); var result = func(42); result.Dump(); } Console.WriteLine("Using MethodInfo.Invoke"); { var method = foo.GetType().GetMethod("Bar"); var result = (ssortingng)method.Invoke(foo, new object[] { 42 }); result.Dump(); } Console.WriteLine("Using the dynamic keyword"); { var dynamicFoo = (dynamic)foo; var result = (ssortingng)dynamicFoo.Bar(42); result.Dump(); } Console.WriteLine("Using CreateDelegate"); { var method = foo.GetType().GetMethod("Bar"); var func = (Func)Delegate.CreateDelegate(typeof(Func), foo, method); var result = func(42); result.Dump(); } Console.WriteLine("Create an expression and comstack it to call the delegate on one instance."); { var method = foo.GetType().GetMethod("Bar"); var thisParam = Expression.Constant(foo); var valueParam = Expression.Parameter(typeof(int), "value"); var call = Expression.Call(thisParam, method, valueParam); var lambda = Expression.Lambda>(call, valueParam); var func = lambda.Comstack(); var result = func(42); result.Dump(); } Console.WriteLine("Create an expression and comstack it to a delegate that could be called on any instance."); { // Note that in this case "Foo" must be known at comstack time, obviously in this case you want // to do more than call a method, otherwise just call it ! var type = foo.GetType(); var method = type.GetMethod("Bar"); var thisParam = Expression.Parameter(type, "this"); var valueParam = Expression.Parameter(typeof(int), "value"); var call = Expression.Call(thisParam, method, valueParam); var lambda = Expression.Lambda>(call, thisParam, valueParam); var func = lambda.Comstack(); var result = func((Foo)foo, 42); result.Dump(); } Console.WriteLine("Create a DynamicMethod and comstack it to a delegate that could be called on any instance."); { // Same thing as the previous expression sample. Foo need to be known at comstack time and need // to be provided to the delegate. var type = foo.GetType(); var method = type.GetMethod("Bar"); var dynamicMethod = new DynamicMethod("Bar_", typeof(ssortingng), new [] { typeof(Foo), typeof(int) }, true); var il = dynamicMethod.GetILGenerator(); il.DeclareLocal(typeof(ssortingng)); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Call, method); il.Emit(OpCodes.Ret); var func = (Func)dynamicMethod.CreateDelegate(typeof(Func)); var result = func((Foo)foo, 42); result.Dump(); } Console.WriteLine("Simulate closure without closures and in a lot more lines..."); { var type = foo.GetType(); var method = type.GetMethod("Bar"); // The Foo class must be public for this to work, the "skipVisibility" argument of // DynamicMethod.CreateDelegate can't be emulated without breaking the .Net security model. var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName("MyAssembly"), AssemblyBuilderAccess.Run); var module = assembly.DefineDynamicModule("MyModule"); var tb = module.DefineType("MyType", TypeAtsortingbutes.Class | TypeAtsortingbutes.Public); var fooField = tb.DefineField("FooInstance", type, FieldAtsortingbutes.Public); var barMethod = tb.DefineMethod("Bar_", MethodAtsortingbutes.Public, typeof(ssortingng), new [] { typeof(int) }); var il = barMethod.GetILGenerator(); il.DeclareLocal(typeof(ssortingng)); il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldfld, fooField); il.Emit(OpCodes.Ldarg_1); // arg il.Emit(OpCodes.Call, method); il.Emit(OpCodes.Ret); var closureType = tb.CreateType(); var instance = closureType.GetConstructors().Single().Invoke(new object[0]); closureType.GetField(fooField.Name).SetValue(instance, foo); var methodOnClosureType = closureType.GetMethod("Bar_"); var func = (Func)Delegate.CreateDelegate(typeof(Func), instance, closureType.GetMethod("Bar_")); var result = func(42); result.Dump(); } } 

La reflection est plus lente. Pour un bon article, consultez cet article.

Ce gars l’a réellement mesuré.

http://www.palmmedia.de/Blog/2012/2/4/reflection-vs-comstackd-expressions-vs-delegates-performance-comparision

En bref: l’expression compilée qui est mise en cache dans une variable statique et réutilisée – effectue 20 fois plus vite que la reflection.