Перейти от лямбда-выражения к выражению легко с помощью вызова метода ...
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
Но я бы хотел превратить Func в выражение, только в редких случаях ...
public void ContainTheDanger(Func<T> dangerousCall)
{
try
{
dangerousCall();
}
catch (Exception e)
{
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
Строка, которая не работает, дает мне ошибку времени компиляции Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
. Явное приведение не разрешает ситуацию. Есть ли средство для этого, которое я не замечаю?
at lambda_method(Closure )
вызова скомпилированного делегата.Ответы:
Ох, это совсем непросто.
Func<T>
представляет собой общее,delegate
а не выражение. Если есть какой-либо способ сделать это (из-за оптимизации и других вещей, сделанных компилятором, некоторые данные могут быть выброшены, поэтому может быть невозможно вернуть исходное выражение), он будет дизассемблировать IL на лету и вывести выражение (что отнюдь не просто). Обработка лямбда-выражений как data (Expression<Func<T>>
) - это чудо, совершаемое компилятором (в основном компилятор строит дерево выражений в коде, а не компилирует его в IL).Связанный факт
Вот почему языки, которые доводят лямбды до крайности (например, Lisp), часто проще реализовать в качестве интерпретаторов . На этих языках код и данные, по сути, одно и то же (даже во время выполнения ), но наш чип не может понять эту форму кода, поэтому мы должны эмулировать такую машину, построив поверх нее интерпретатор, который ее понимает ( выбор, сделанный Lisp-подобными языками) или в некоторой степени жертвуя мощностью (код больше не будет точно соответствовать данным) (выбор, сделанный C #). В C # компилятор создает иллюзию обработки кода как данных, позволяя интерпретировать лямбда-выражения как code (
Func<T>
) и data (Expression<Func<T>>
) во время компиляции .источник
eval
вам нужно будет запустить компилятор, но в остальном нет никаких проблем с этим.Expression
о вашем действии оболочки, но не будет иметь никакой информации о дереве выражений о внутренних компонентахdangerousCall
делегата.источник
Func
будут скрыты в новом выражении. Это просто добавляет один уровень данных поверх кода; вы можете пройти по одному слою, чтобы найти свой параметрf
без дополнительной информации, так что вы там, где начали.Что вам, вероятно, следует сделать, так это изменить этот метод. Возьмите Expression>, скомпилируйте и запустите. Если это не удается, у вас уже есть выражение, на которое стоит обратить внимание.
Очевидно, вам нужно учитывать влияние этого на производительность и определять, действительно ли это нужно делать.
источник
Однако вы можете пойти другим путем с помощью метода .Compile () - не уверен, что это полезно для вас:
источник
Если вам иногда нужно выражение, а иногда - делегат, у вас есть 2 варианта:
Expression<...>
версию, и просто.Compile().Invoke(...)
ее, если вам нужен делегат. Очевидно, это дорого.источник
NJection.LambdaConverter - это библиотека, которая преобразует делегатов в выражение
источник
источник
call.Target
часть, которая меня убивала. Он работал годами, а потом внезапно перестал работать и начал жаловаться на статический / нестатический бла-бла. В любом случае, спасибо!JB Evain из команды Cecil Mono делает некоторые успехи, чтобы сделать это возможным.
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
источник
+ Изменить
к
источник