Создание дерева выражений, которое вызывает метод - PullRequest
8 голосов
/ 26 июля 2010

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

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<Func<int, int, int>> lambda = (a, b) => MyFunc(a, b);
var expr = Expression.Invoke(lambda, c1, c2);

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

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

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

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

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

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

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

По крайней мере, это более вероятно, чтобы поймать проблемы во время компиляции.Но он все равно платит за рефлексию во время выполнения, не так ли?

1 Ответ

12 голосов
/ 26 июля 2010

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

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

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

...