Search code examples
c#performancedelegatesexpressiondynamic-invoke

Fast way to get Expression method call target


Given the following code line of code,

Expression<Action> expression = () => target.ToString();

is there a fast way to obtain the target object?

Below code works

public object GetExpressionTarget<T>(Expression<T> expression)
{
    MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
    LambdaExpression theTarget = Expression.Lambda(methodCall.Object, null);
    Delegate compiled = theTarget.Compile();

    return compiled.DynamicInvoke();    }

but is very, very slow.


Is there a faster way to get the target of a method call expression?


Benchmarking my code (GetDelegate, DelegateCompile and DelegateDynamicInvoke) as well as @IvanStoev's code (GetFunc, FuncCompile and FuncInvoke) yields this result:

|                Method |           Mean |         Error |        StdDev |
|---------------------- |----------------|---------------|---------------|
|       DelegateCompile | 165,068.477 ns | 2,671.3001 ns | 2,498.7358 ns |
|           FuncCompile | 160,956.199 ns | 2,133.5343 ns | 1,995.7093 ns |
| DelegateDynamicInvoke |   1,148.191 ns |    11.7213 ns |    10.9642 ns |
|            FuncInvoke |       3.040 ns |     0.0264 ns |     0.0247 ns |

So, Invoke is actually quite faster than DynamicInvoke, but the bottleneck is actually the Compile call. Is there a way to get the target object without having to compile expressions?

Benchmark code:

public class Program
{
    private Delegate @delegate;
    private Func<object> func;

    private static Delegate GetDelegate(Expression<Action> expression)
    {
        MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
        return Expression.Lambda(methodCall.Object, null).Compile();
    }

    private static Func<object> GetFunc(Expression<Action> expression)
    {
        MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
        return Expression.Lambda<Func<object>>(methodCall.Object).Compile();
    }

    [GlobalSetup]
    public void Setup()
    {
        object o = new object();
        Expression<Action> expression = () => o.ToString();

        this.@delegate = Program.GetDelegate(expression);
        this.func = Program.GetFunc(expression);
    }

    [Benchmark]
    public void DelegateCompile()
    {
        object o = new object();
        Expression<Action> expression = () => o.ToString();

        Program.GetDelegate(expression);
    }

    [Benchmark]
    public void FuncCompile()
    {
        object o = new object();
        Expression<Action> expression = () => o.ToString();

        Program.GetFunc(expression);
    }

    [Benchmark]
    public void DelegateDynamicInvoke()
    {
        this.@delegate.DynamicInvoke();
    }

    [Benchmark]
    public void FuncInvoke()
    {
        this.func.Invoke();
    }

    public static void Main(string[] args)
    {
        BenchmarkRunner.Run<Program>();
    }
}

Solution

  • The only way I can think of to avoid the time costly Compile operation is to evaluate the expression content recursively using reflection.

    Doing this generically (handling all the cases) is a complicated task. Currently there are more than 80 ExpressionTypes, all of them with different semantics (well, some fall into categories with corresponding base classes). In order to handle them all, one should probably create custom ExpressionVisitor and implement evaluation engine (probably with some sort of an evaluation stack).

    In other words, a lot of work/code.

    However... If we limit the expressions to 2 types - ConstantExpression (constant value) and MemberExpression (field or property of a constant value), then there is a relatively easy solution. The method in question already contains assumption about the passed Expression<Action> and the sample expression target (which is a closure) falls into constant value field category.

    The main work is done in a private recursive method as follows:

    static object Evaluate(Expression expression)
    {
        if (expression == null)
            return null;
        if (expression is ConstantExpression constExpression)
            return constExpression.Value;
        if (expression is MemberExpression memberExpression)
        {
            var target = Evaluate(memberExpression.Expression);
            if (memberExpression.Member is FieldInfo field)
                return field.GetValue(target);
            if (memberExpression.Member is PropertyInfo property)
                return property.GetValue(target);
        }
        throw new NotSupportedException();
    }
    

    and the method in question using it would be

    public object GetExpressionTarget<T>(Expression<T> expression)
    {
        var methodCall = (MethodCallExpression)expression.Body;
        return Evaluate(methodCall.Object);
    }
    

    I have no performance comparison results, but even though this is using reflection, it should be much faster than the Compile which uses reflection and dynamic IL code emit, not counting creation of a DynamicMethod and delegate for invoking it.