Dans C#, L'API D'Expression est-elle meilleure que la réflexion?

de nos jours, J'explore C# Expression APIs. Je pourrais donc avoir besoin d'aide pour comprendre comment cela fonctionne, y compris la différence entre L'Expression et la réflexion. Je veux aussi comprendre si les Expressions sont simplement du sucre syntaxique, ou sont-elles vraiment meilleures que la réflexion performance-wise ?

de Bons exemples ainsi que des liens vers de bons articles serait appréciée. :- )

38
demandé sur Nawaz 2011-01-26 13:05:09

3 réponses

concernant l'appel d'une méthode:

  • L'appel Direct ne peut pas être battu en termes de vitesse.
  • en utilisant L'expression API est globalement similaire à l'utilisation de Reflection.Emit ou Delegate.CreateDelegate en termes de vitesse (de petites différences pourraient être mesurées; comme toujours optimiser pour la vitesse sans mesures et objectifs est inutile).

    ils génèrent tous IL et le framework le compilera en code natif à un moment donné. Mais vous avez encore le payer le coût d'un niveau d'indirection pour appeler le délégué et un appel de méthode à l'intérieur de votre délégué.

    L'API d'expression est plus limitée, mais un ordre de grandeur plus simple à utiliser, car il ne nécessite pas que vous appreniez IL.

  • le langage dynamique Runtime soit utilisé directement ou via le mot-clé dynamic de C# 4 Ajouter un peu de overhead mais rester près du code d'émission car il cache la plupart des vérifications liées aux types de paramètres, l'accès et le reste.

    Lorsqu'il est utilisé via le mot-clé dynamic , il est également obtenir la syntaxe la plus correcte car il ressemble à un appel de méthode normale. Mais si vous utilisez dynamic vous êtes limité aux appels de méthode alors que la Bibliothèque est capable de faire beaucoup plus (Voir IronPython )

  • System.Reflection.MethodInfo.Invoke est lent : en plus de ce que d'autres méthodes ont besoin pour vérifier les droits d'accès, vérifier les arguments count, type, ... contre le MethodInfo à chaque appel de la méthode.

Jon Skeet obtient également quelques bons points dans cette réponse: délégué.CreateDelegate vs DynamicMethod vs Expression


Certains échantillons, la même chose fait de différentes façons.

vous pouvez déjà voir à partir du nombre de lignes et de la complexité quelles solutions sont faciles à entretenir et qui doivent être évitées d'une maintenance à long terme point de vue.

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

PS: Dump est une méthode LINQPad .

public class Foo
{
    public string Bar(int value) { return value.ToString(); }
}

void Main()
{
    object foo = new Foo();

    // We have an instance of something and want to call a method with this signature on it :
    // public string 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<int, string> 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 = (string)method.Invoke(foo, new object[] { 42 });
        result.Dump();
    }
    Console.WriteLine("Using the dynamic keyword");
    {
        var dynamicFoo = (dynamic)foo;
        var result = (string)dynamicFoo.Bar(42);
        result.Dump();
    }
    Console.WriteLine("Using CreateDelegate");
    {
        var method = foo.GetType().GetMethod("Bar");
        var func = (Func<int, string>)Delegate.CreateDelegate(typeof(Func<int, string>), foo, method);
        var result = func(42);
        result.Dump();
    }
    Console.WriteLine("Create an expression and compile 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<Func<int, string>>(call, valueParam);
        var func = lambda.Compile();
        var result = func(42);
        result.Dump();
    }
    Console.WriteLine("Create an expression and compile it to a delegate that could be called on any instance.");
    {
        // Note that in this case "Foo" must be known at compile 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<Func<Foo, int, string>>(call, thisParam, valueParam);
        var func = lambda.Compile();
        var result = func((Foo)foo, 42);
        result.Dump();
    }
    Console.WriteLine("Create a DynamicMethod and compile it to a delegate that could be called on any instance.");
    {
        // Same thing as the previous expression sample. Foo need to be known at compile time and need
        // to be provided to the delegate.

        var type = foo.GetType();
        var method = type.GetMethod("Bar");

        var dynamicMethod = new DynamicMethod("Bar_", typeof(string), new [] { typeof(Foo), typeof(int) }, true);
        var il = dynamicMethod.GetILGenerator();
        il.DeclareLocal(typeof(string));
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Call, method);
        il.Emit(OpCodes.Ret);
        var func = (Func<Foo, int, string>)dynamicMethod.CreateDelegate(typeof(Func<Foo, int, string>));
        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", TypeAttributes.Class | TypeAttributes.Public);

        var fooField = tb.DefineField("FooInstance", type, FieldAttributes.Public);
        var barMethod = tb.DefineMethod("Bar_", MethodAttributes.Public, typeof(string), new [] { typeof(int) });
        var il = barMethod.GetILGenerator();
        il.DeclareLocal(typeof(string));
        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<int, string>)Delegate.CreateDelegate(typeof(Func<int, string>), instance,
            closureType.GetMethod("Bar_"));
        var result = func(42);
        result.Dump();
    }
}
45
répondu Julien Roncaglia 2017-05-23 12:34:12

la réflexion est plus lente. Pour un bon article sur elle voir ce article.

4
répondu Cornelius 2011-01-26 10:12:14

ce type l'a mesuré.

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

en bref: expression compilée qui est mise en cache à un var statique et réutilisé - effectue 20 fois plus vite que la réflexion.

2
répondu jazzcat 2017-04-24 00:22:12