48 votes

En C #, l'API d'expression est-elle meilleure que la réflexion

Aujourd'hui, j'explore les API d'expression C #. Je pourrais donc utiliser de l'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 ne sont que du sucre syntaxique, ou sont-elles en effet meilleures que les performances de Reflection?

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

53voto

VirtualBlackFox Points 9565

Concernant l'appel d'une méthode :

  • Appel Direct ne peuvent pas être battus en termes de vitesse.
  • À l'aide de l'Expression de l'API est globalement similaire à l'utilisation d' Reflection.Emit ou Delegate.CreateDelegate en termes de vitesse (Petites différences peuvent être mesurés; comme toujours, de l'optimisation de vitesse sans mesures et des objectifs est inutile).

    Ils produisent tous IL et dans le cadre de la compilation en code natif à un certain point. Mais vous continuez à 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'expression de l'API est plus limitée, mais l'ordre de grandeur plus simple à utiliser car il ne nécessite pas de vous apprendre IL.

  • Le Dynamic Language Runtime utilisée soit directement ou par l'intermédiaire de l' dynamic de mots clés de C# 4 ajouter un peu de surcharge, mais rester près émettant code tel qu'il cache la plupart des contrôles liés à des types de paramètres, l'accès et le reste.

    Lorsqu'il est utilisé via l' dynamic mot-clé c'est aussi obtenir la plus élégante de la syntaxe comme il l'air comme un simple appel de méthode. Mais si vous utilisez dynamique, vous êtes limité à des appels de méthode alors que la bibliothèque est en mesure de faire beaucoup plus (Voir IronPython)

  • System.Reflection.MethodInfo.Invoke est lent : en plus de ce que d'autres méthodes ne il faut vérifier les droits d'accès, de vérifier les arguments de comptage, type, ... contre l' MethodInfo à chaque appel de la méthode.

Jon Skeet également obtenir 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 pouvait déjà le voir à partir de la ligne de comte et de la complexité de la sorcière solutions sont faciles à entretenir et sorcière doivent être évités à long terme point de vue entretien.

La plupart des échantillons sont inutiles, mais elles témoignent de la base de génération de code classes / la syntaxe de C#, pour plus d'info il y a toujours la MSDN

PS: Dump est un LINQPad méthode.

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

3voto

Cornelius Points 1374

La réflexion est plus lente. Pour un bon article à ce sujet, consultez cet article.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X