Создание дерева выражений, которое называет метод

Действительно ли возможно создать дерево выражений, которое непосредственно называет метод? Например, рассмотрите следующий метод:

public static int MyFunc(int a, int b)
{
    return a + b;
}

Я хотел бы создать дерево выражений, которое называет MyFunc с параметрами a=1 и b=2. Один способ выполнить это с отражением:

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);

Однако это невыгодно, потому что отражение является медленным и поворачивает то, что должно быть ошибками времени компиляции в ошибки времени выполнения.

Я мог использовать следующий подход вместо этого:

Expression> lambda = (a, b) => MyFunc(a, b);
var expr = Expression.Invoke(lambda, c1, c2);

Но это все еще не, что я хочу, потому что это переносит метод в лямбда-выражение вместо того, чтобы назвать его непосредственно.

Хорошее решение могло бы быть основано на делегате, как это:

Func del = Program.MyFunc;
var expr = Expression.Invoke(del, c1, c2);

К сожалению, это не компилирует потому что del делегат, а не выражение. Там какой-либо путь состоит в том, чтобы создать выражение от делегата? (Обратите внимание, что я знаю цель делегата во время компиляции, таким образом, мне не нужен вид гибкости, описанной здесь: Деревья выражений и Вызов Делегата.)

Решение неделегата было бы также прекрасно, пока оно называет целевой метод максимально непосредственно.

Обновление: Это также работает, но это все еще полагается на отражение:

Func del = Program.MyFunc;
var expr = Expression.Call(del.Method, c1, c2);

По крайней мере, это, более вероятно, поймает проблемы во время компиляции. Но это все еще платит цену во время выполнения за отражение, не так ли?

8
задан Community 23 May 2017 в 10:30
поделиться

1 ответ

Пока вы вызываете .Compile в лямбда-выражении и сохраняете (и повторно используете) делегат, вы платите цену отражения только один раз.

var c1 = Expression.Constant(1);
var c2 = Expression.Constant(2);
var expr = Expression.Call(typeof(Program).GetMethod("MyFunc"), c1, c2);
Func<int> func = Expression.Lambda<Func<int>>(expr).Compile();
// ** now store func and re-use it **

Однако, чтобы получить голый делегат для только этот метод, вы можете использовать:

var method = typeof(Program).GetMethod("MyFunc");
Func<int, int, int> func = (Func<int, int, int>) Delegate.CreateDelegate(
         typeof(Func<int, int, int>), method);

конечно, тогда вы будете вынуждены предоставить константы в вызывающей стороне.

Другой вариант - DynamicMethod , но пока вы кэшируете последний делегат, это не будет значительно быстрее. Он предлагает большую гибкость (ценой сложности), но, похоже, здесь проблема не в этом.

12
ответ дан 5 December 2019 в 14:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: