Выражение и делегат в C#
У меня есть ниже код, который является псевдокодом. Я хочу, чтобы эта функция могла принимать тип 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");
}
Я уверен, что T это тип Action. (Т может быть Action
,Action<int>
,так далее.). Параметр d
массив динамического типа, который отправляется для вызова.
Но я не знаю, как закончить код. Я уверен, что это нелегко реализовать. Возможно, это не может быть правдой в C#
2 ответа
Вы не можете использовать Invoke
если вы не знаете точную подпись. Вы можете, однако, использовать DynamicInvoke
, например:
((Delegate)exp.Compile()).DynamicInvoke(d);
Обратите внимание, что dynamic
в вышесказанном не имеет смысла - d
с таким же успехом может быть object[]
,
Другой, немного более сложный подход - это скомпилировать его как Func<object[]>
и переписать выражение (ExpressionVisitor
) заменить "параметр n" (из оригинала exp
) с p[n]
, где p
это один ParameterExpression
а также n
это ConstantExpression
из n
, Это может быть полезно, если вы собираетесь хранить и агрессивно повторно использовать скомпилированную лямбду. Но в вашем конкретном сценарии вы компилируете за вызов, так что это не принесет пользы.
Вот пример, но он в основном предназначен для более поздних читателей с похожими сценариями, но где скомпилированный делегат используется повторно; "Преимущество" этого переписывания заключается в том, что оно позволяет избежать влияния на производительность Delegate.DynamicInvoke
, сохраняя при этом object[] => object
подпись Delegate.DynamicInvoke
; но это будет полезно только в том случае, если делегат используется несколько раз. На данный момент (скомпилировано за вызов) большая часть "работы" здесь будет в выражении-компиляции и 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");
}
А потом:
F<Action<int>>(i => Console.WriteLine(i), 5);
или же:
F<Action<string, int>>((s, i) => Console.WriteLine("{0} : {1}", s, i), "Hello", 5);