Действительно ли возможно создать дерево выражений, которое непосредственно называет метод? Например, рассмотрите следующий метод:
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);
По крайней мере, это, более вероятно, поймает проблемы во время компиляции. Но это все еще платит цену во время выполнения за отражение, не так ли?
Пока вы вызываете .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
, но пока вы кэшируете последний делегат, это не будет значительно быстрее. Он предлагает большую гибкость (ценой сложности), но, похоже, здесь проблема не в этом.