Выражение и делегат в 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);
Другие вопросы по тегам