In C#, Is Expression API better than Reflection
Asked Answered
O

3

48

Nowadays, I'm exploring C# Expression APIs. So I could use some help understanding how it works, including the difference between Expression and Reflection. I also want to understand if Expressions are merely syntactic sugar, or are they indeed better than Reflection performance-wise?

Good examples as well as links to good articles would be appreciated. :-)

Omari answered 26/1, 2011 at 10:5 Comment(4)
By reflection you mean calling things using the System.Reflection.MethodInfo class for example or emitting via Reflection.Emit ?Minne
@VirtualBlackFox: by reflection I mean things which can be done with Reflection, should they be done by Expression? That includes I think code-generation also. :-)Omari
Best way to tackle this is by not assuming they have anything in common. If you mean Reflection.Emit, that already gets used by Expressions.Fadiman
This question needs to be narrowed down.Suppletion
M
53

Regarding calling one method :

  • Direct call can't be beaten speed-wise.
  • Using Expression API is globally similar to using Reflection.Emit or Delegate.CreateDelegate speed-wise (Small differences could be measured; as always optimizing for speed without measurements and goals is useless).

    They all generate IL and the framework will compile it to native code at some point. But you still pay the cost of one indirection level for calling the delegate and one method call inside your delegate.

    The expression API is more limited, but an order of magnitude simpler to use, as it doesn't require you to learn IL.

  • The Dynamic Language Runtime either used directly or via the dynamic keyword of C# 4 add a little overhead but stay near emitting code as it cache most checks related to parameter types, access and the rest.

    When used via the dynamic keyword it's also get the neatest syntax as it looks like a normal method call. But if you use dynamic you are limited to method calls while the library is able to do a lot more (See IronPython)

  • System.Reflection.MethodInfo.Invoke is slow : in addition to what other methods do it need to check access rights, check arguments count, type, ... against the MethodInfo each time you call the method.

Jon Skeet also get some good points in this answer : Delegate.CreateDelegate vs DynamicMethod vs Expression


Some samples, the same thing done different ways.

You could already see from the line count and complexity which solutions are easy to maintain and which should be avoided from a long term maintenance standpoint.

Most of the samples are pointless but they demonstrate the basic code generation classes / syntaxes of C#, for more info there is always the MSDN

PS: Dump is a LINQPad method.

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();
    }
}
Minne answered 26/1, 2011 at 10:33 Comment(7)
I may add samples while in lunch break :D Reflection.Emit is especially tricky to write sample on without at least testing in Linqpad.Minne
Expression API is not so limited starting from .NET 4.0. You can create expressions equivalent to almost any C# code with it.Trixi
If I have a base class which needs to call methods of the subclass dynamically (with getting the names dynamically) then I cannot use direct or 'dynamic' since they requre compile time knowledge of the name. I use MethodInfo.Invoke (and PropertyInfo.SetValue) for simplicity sake. For every save and load operation (not realtime) one of these is called on every property and on some method of the subclass. Expression really seems like overkill, but does CreateDelegate offer me anything which MemberInfos don't?Spearman
@Spearman : Normally it's faster. To use the returned delegate as I did in my sample you need to know the return and parameter types at compilation time but the advantage is that you skip lots of checks once casted to the correct delegate type (arguments type, count, security, ...)Minne
ah. return and parameter types at compile time. :) That I don't have. Thanks, now I understand the "when to use what"...Spearman
@VirtualBlackFox: You said i'll add Reflection.Emit samples later.... I'm waiting for it :-)Omari
@Nawaz: I sent myself a mail with my current state of the code and never finished it, not its done so I add it but anyway the code is really simple once you know a bit of IL.Minne
D
4

This guy actually measured it.

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

In short: compiled expression that is cached to a static var and reused - performs much faster than reflection.

Disney answered 24/4, 2017 at 0:22 Comment(0)
I
3

Reflection does perform slower. For a good article on it see this article.

Impassioned answered 26/1, 2011 at 10:12 Comment(1)
This is misleading since you can always compile a reflected method call etc with Delegate.CreateDelegate, for example. In that situation it'll be much the same as a compiled expression.Secretary

© 2022 - 2024 — McMap. All rights reserved.