表达和委托在c# [英] Expression and delegate in c#

查看:137
本文介绍了表达和委托在c#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码是伪代码。
我想让这个函数可以接受一个Expresstion类型,编译这个表达式并用正确的参数调用它。

  public static void f< T>(表达式< T> exp,params dynamic [] d)
{
Console.WriteLine(begin);
exp.Compile()。Invoke(d [0],d [1] .....); //这是伪代码

Console.WriteLine(end );
}

我确定T是一个Action类型。 (T可以是 Action Action< int> 等)。参数 d 是动态类型的数组,它被发送到调用。



但是我不知道如何完成代码我相信这不容易实现。也许在c#中不可能这样做

解决方案

你不能使用 Invoke 除非你知道确切的签名。但是,您可以使用 DynamicInvoke ,例如:

 (( 。代表)exp.Compile())DynamicInvoke(D); 

请注意,上述的动态没有目的 - d 也可以是 object []


$ b $另一个稍微复杂一点的方法是把它编译为一个 Func< object []> ,然后重写表达式( ExpressionVisitor )用code> p [n]替换参数n(从原来的 exp ) / code>,其中 p 是一个 ParameterExpression n n ConstantExpression 。如果您将要存储并积极地重新使用编译的lambda,这可能是有利的。但是在您的具体方案中,您正在编译每次调用,所以这将无益。



这是一个例子,但这主要是为了后来的读者有类似的场景,但编译代表被重新使用的地方;这种重写的优势是避免 Delegate.DynamicInvoke 的性能影响,同时保留 object [] =>对象签名 Delegate.DynamicInvoke ;但是,如果代理被多次使用,这只会有用。目前(每个调用编译),这里的大部分工作将在表达式编译和JIT编译中。

  using System; 
使用System.Collections.Generic;
使用System.Linq.Expressions;
static class Program {
static void Main(){
Expression< Func< int,float,double>> exp =(i,f)=>我* 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);
字典<表达式,表达式> 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));
返回Expression.Lambda }
class ReplaceVisitor:ExpressionVisitor {
private readonly Dictionary< Expression,Expression>抬头;
public ReplaceVisitor(Dictionary< Expression,Expression> lookup){
if(lookup == null)throw new ArgumentNullException(lookup);
this.lookup = lookup;
}
public override表达式访问(表达式节点){
表达式找到;
return lookup.TryGetValue(node,out found)?发现
:base.Visit(node);
}
}
}


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#

解决方案

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

这篇关于表达和委托在c#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆