Expression and delegate in c#

北战南征 提交于 2019-12-11 10:18:01

问题


I have below code that is a pseudo-code. I want to make this function can accept a Expresstion type, compile this expression and invoke it with proper parameters.

public static void f<T>(Expression<T> exp, params dynamic[] d)
{
    Console.WriteLine("begin");
    exp.Compile().Invoke(d[0],d[1].....);//this is pseudo-code

    Console.WriteLine("end");
}

I'm sure the T is an Action type. (T can be Action,Action<int>,etc.). The parameter d is a array of dynamic type, which is sent to invoke.

But I don't know how to finish the code. I'm sure that's not easy to implement it. Perhaps it can't be true in c#


回答1:


You can't use Invoke unless you know the exact signature. You can, however, use DynamicInvoke, for example:

((Delegate)exp.Compile()).DynamicInvoke(d);

Note that the dynamic in the above serves no purpose - d could just as well be object[].

The other, slightly more complicated, approach - would be to compile it as a Func<object[]>, and re-write the expression (ExpressionVisitor) to replace "parameter n" (from the original exp) with p[n], where p is a single ParameterExpression and n is a ConstantExpression of n. This might be advantageous if you were going to store and aggressively re-use the compiled lambda. But in your specific scenario you are compiling per call, so this would have no benefit.

Here's an example, but this is mainly intended for later readers with similar scenarios, but where the compiled delegate is re-used; the "advantage" of this re-writing is that it avoids the performance impact of Delegate.DynamicInvoke, while retaining the object[] => object signature of Delegate.DynamicInvoke; but this will only be useful if the delegate is being used multiple times. At the moment (compiled per call) most of the "work" here is going to be in the expression-compile and JIT-compile.

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
static class Program {
    static void Main() {
        Expression<Func<int, float, double>> exp = (i, f) => i * f;
        var func = CompileToBasicType(exp);

        object[] args = { 3, 2.3F };
        object result = func(args); // 6.9 (double)
    }

    static Func<object[], object> CompileToBasicType(LambdaExpression exp) {
        ParameterExpression arg =
            Expression.Parameter(typeof(object[]), "args");
        Dictionary<Expression, Expression> lookup =
            new Dictionary<Expression, Expression>();
        int i = 0;
        foreach (var p in exp.Parameters) {
            lookup.Add(p, Expression.Convert(Expression.ArrayIndex(
                arg, Expression.Constant(i++)), p.Type));
        }
        var body = Expression.Convert(
            new ReplaceVisitor(lookup).Visit(exp.Body), typeof(object));
        return Expression.Lambda<Func<object[], object>>(body, arg).Compile();
    }
    class ReplaceVisitor : ExpressionVisitor {
        private readonly Dictionary<Expression, Expression> lookup;
        public ReplaceVisitor(Dictionary<Expression, Expression> lookup) {
            if (lookup == null) throw new ArgumentNullException("lookup");
            this.lookup= lookup;
        }
        public override Expression Visit(Expression node) {
            Expression found;
            return lookup.TryGetValue(node, out found) ? found
                : base.Visit(node);
        }
    }
}



回答2:


public static void F<T>(Expression<T> exp, params object[] d)
{
    Console.WriteLine("begin");

    var del = exp.Compile() as Delegate;
    del.DynamicInvoke(d);

    Console.WriteLine("end");
}

And then:

F<Action<int>>(i => Console.WriteLine(i), 5);

or:

F<Action<string, int>>((s, i) => Console.WriteLine("{0} : {1}", s, i), "Hello", 5);


来源:https://stackoverflow.com/questions/16663567/expression-and-delegate-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!