.net öffentliche - In C#ist Expression API besser als Reflection




auf nicht (4)

Heute erkunde ich C # Expression APIs. So könnte ich etwas Hilfe verwenden, um zu verstehen, wie es funktioniert, einschließlich des Unterschieds zwischen Ausdruck und Reflexion. Ich möchte auch verstehen, ob Expressions nur syntaktischer Zucker sind, oder sind sie tatsächlich besser als Reflection Performance-weise ?

Gute Beispiele sowie Links zu guten Artikeln wären willkommen. :-)


Answers

In Bezug auf den Aufruf einer Methode:

  • Direkter Anruf kann nicht schnell geschlagen werden.
  • Die Verwendung der Expression-API ist im Allgemeinen ähnlich wie die Verwendung von Reflection.Emit oder Delegate.CreateDelegate in Reflection.Emit Geschwindigkeit (kleine Unterschiede könnten gemessen werden, da die Optimierung der Geschwindigkeit ohne Messungen und Ziele immer nutzlos ist).

    Sie alle erzeugen IL und das Framework wird es irgendwann zu nativem Code kompilieren. Sie zahlen jedoch immer noch die Kosten für eine Indirektionsebene für den Aufruf des Delegaten und einen Methodenaufruf in Ihrem Delegaten.

    Der Ausdruck API ist begrenzter, aber eine Größenordnung einfacher zu verwenden, da Sie IL nicht lernen müssen.

  • Die Dynamic Language Runtime, die entweder direkt oder über das dynamic Schlüsselwort von C # 4 verwendet wird, fügt einen kleinen Overhead hinzu, bleibt aber in der Nähe des emittierenden Codes, da sie die meisten Prüfungen in Bezug auf Parametertypen, Zugriff und den Rest zwischenspeichert.

    Wenn es über das dynamic Schlüsselwort verwendet wird, erhält es auch die sauberste Syntax, da es wie ein normaler Methodenaufruf aussieht. Wenn Sie jedoch dynamic verwenden, sind Sie auf Methodenaufrufe beschränkt, während die Bibliothek viel mehr kann (siehe IronPython ).

  • System.Reflection.MethodInfo.Invoke ist langsam: Zusätzlich zu den anderen Methoden, die zum Überprüfen von Zugriffsrechten erforderlich sind, überprüfen Sie jedes Mal, wenn Sie die Methode aufrufen, Argumente count, type, ... für die MethodInfo .

Jon Skeet erhält auch einige gute Punkte in dieser Antwort: Delegate.CreateDelegate vs DynamicMethod vs Expression

Einige Samples, das gleiche taten verschiedene Wege.

Sie konnten bereits anhand der Anzahl und Komplexität der Linien erkennen, welche Lösungen einfach zu warten sind und welche aus Sicht der langfristigen Wartung vermieden werden sollten.

Die meisten Beispiele sind sinnlos, aber sie zeigen die grundlegenden Codegenerierungsklassen / -syntaxen von C #, für weitere Informationen gibt es immer das MSDN

PS: Dump ist eine LINQPad Methode.

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


Die Reflexion funktioniert langsamer. Für einen guten Artikel darüber siehe this Artikel.


Benutzen:

    string text = "string";
    byte[] array = System.Text.Encoding.UTF8.GetBytes(text);

The result is:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103






c# .net reflection expression