简体   繁体   English

C#中的表达式和委托

[英]Expression and delegate in c#

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. 我想使此函数可以接受Expresstion类型,编译此表达式并使用适当的参数调用它。

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是动作类型。 (T can be Action , Action<int> ,etc.). (T可以是ActionAction<int>等)。 The parameter d is a array of dynamic type, which is sent to invoke. 参数d是动态类型的数组,该数组被发送以调用。

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# 也许在C#中可能不正确

You can't use Invoke unless you know the exact signature. 除非您知道确切的签名,否则您不能使用Invoke You can, however, use DynamicInvoke , for example: 但是,您可以使用DynamicInvoke ,例如:

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

Note that the dynamic in the above serves no purpose - d could just as well be object[] . 注意,上面的dynamic没有任何作用d也可以是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 . 另一种稍微复杂一点的方法是将其编译为Func<object[]> ,然后重新编写表达式( ExpressionVisitor )以用p[n]替换“参数n”(来自原始exp ),其中p是一个单一的ParameterExpressionn是一个ConstantExpressionn This might be advantageous if you were going to store and aggressively re-use the compiled lambda. 如果您将要存储并积极地重新使用已编译的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 ; 的此再写入的“优势”是它避免了对性能的影响Delegate.DynamicInvoke ,同时保留了object[] => object的签名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. 目前(按调用编译),此处的大部分“工作”将在表达式编译和JIT编译中进行。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM