Saltar al contenido

En C#, ¿Es Expression API mejor que Reflection?

Nuestro equipo de expertos pasados varios días de investigación y recopilar de datos, obtuvieron la respuesta, deseamos que resulte de utilidad para tu trabajo.

Solución:

Con respecto a llamar a un método:

  • La llamada directa es inmejorable en cuanto a velocidad.
  • Usar Expression API es globalmente similar a usar Reflection.Emit o Delegate.CreateDelegate en cuanto a la velocidad (se pueden medir pequeñas diferencias; como siempre, optimizar la velocidad sin mediciones y objetivos es inútil).

    Todos generan IL y el marco lo compilará en código nativo en algún momento. Pero aún paga el costo de un nivel de indirección para llamar al delegado y una llamada de método dentro de su delegado.

    La expresión API es más limitada, pero un orden de magnitud más simple de usar, ya que no requiere que aprenda IL.

  • Dynamic Language Runtime se usa directamente o a través del dynamic La palabra clave de C# 4 agrega un poco de sobrecarga pero permanece cerca de la emisión de código, ya que almacena en caché la mayoría de las comprobaciones relacionadas con los tipos de parámetros, el acceso y el resto.

    Cuando se utiliza a través de la dynamic palabra clave también obtiene la sintaxis más ordenada, ya que parece una llamada de método normal. Pero si usa dinámica, está limitado a las llamadas a métodos, mientras que la biblioteca puede hacer mucho más (consulte IronPython)

  • System.Reflection.MethodInfo.Invoke es lento: además de qué otros métodos necesita para verificar los derechos de acceso, verificar el recuento de argumentos, el tipo, … contra el MethodInfo cada vez que llame al método.

Jon Skeet también obtuvo algunos buenos puntos en esta respuesta: Delegate.CreateDelegate vs DynamicMethod vs Expression


Algunas muestras, lo mismo hecho de diferentes maneras.

A partir del número de líneas y la complejidad, ya puede ver qué soluciones son fáciles de mantener y cuáles deben evitarse desde el punto de vista del mantenimiento a largo plazo.

La mayoría de los ejemplos no tienen sentido, pero demuestran las clases/sintaxis básicas de generación de código de C#, para obtener más información, siempre está el MSDN

PD: Dump es un método 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 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)Delegate.CreateDelegate(typeof(Func), 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>(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>(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)dynamicMethod.CreateDelegate(typeof(Func));
        var result = func((Foo)foo, 42);
        result.Dump();
    
    Console.WriteLine("Simulate closure without closures and in a lot more lines...");
     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)Delegate.CreateDelegate(typeof(Func), instance,
            closureType.GetMethod("Bar_"));
        var result = func(42);
        result.Dump();
    

La reflexión funciona más lentamente. Para un buen artículo sobre esto ver este artículo.

Este tipo realmente lo midió.

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

En resumen: expresión compilada que es en caché a un static var y reutilizado: funciona mucho más rápido que la reflexión.

Eres capaz de añadir valor a nuestro contenido asistiendo con tu experiencia en las interpretaciones.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *