表达和委托在c# [英] Expression and delegate in 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屋!