Przejście od lambdy do wyrażenia jest łatwe przy użyciu wywołania metody ...
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
Ale chciałbym zmienić Func w wyrażenie, tylko w rzadkich przypadkach ...
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());
}
Linia, która nie działa, wyświetla błąd w czasie kompilacji Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
. Jawna obsada nie rozwiązuje sytuacji. Czy istnieje możliwość zrobienia tego, czego nie zauważam?
at lambda_method(Closure )
do wywołania skompilowanego delegata.Odpowiedzi:
Ooh, to wcale nie jest łatwe.
Func<T>
reprezentuje rodzaj,delegate
a nie wyrażenie. Jeśli można to zrobić w jakikolwiek sposób (z powodu optymalizacji i innych rzeczy wykonanych przez kompilator, niektóre dane mogą zostać wyrzucone, więc odzyskanie oryginalnego wyrażenia może być niemożliwe), byłby to demontaż IL w locie i wywnioskować wyrażenie (co wcale nie jest łatwe). Traktowanie wyrażeń lambda jako danych (Expression<Func<T>>
) jest magią wykonywaną przez kompilator (w zasadzie kompilator buduje drzewo wyrażeń w kodzie zamiast kompilować je do IL).Powiązany fakt
Dlatego języki, które wypychają lambdy do skrajności (takie jak Lisp), są często łatwiejsze do zaimplementowania jako interpretery . W tych językach kod i dane są zasadniczo tym samym (nawet w czasie wykonywania ), ale nasz chip nie może zrozumieć tej formy kodu, więc musimy emulować taką maszynę, budując na niej interpreter, który ją rozumie ( wybór dokonany przez Lisp podobnie jak języki) lub poświęcenie mocy (kod nie będzie już dokładnie równy danym) do pewnego stopnia (wybór dokonany przez C #). W języku C # kompilator daje złudzenie traktowania kodu jako danych, umożliwiając interpretowanie wyrażeń lambda jako code (
Func<T>
) i data (Expression<Func<T>>
) w czasie kompilacji .źródło
eval
, musisz uruchomić kompilator, ale poza tym nie ma z tym żadnego problemu.Expression
informacje o akcji opakowania, ale nie zawierałoby informacji o drzewie wyrażeń o elementach wewnętrznychdangerousCall
delegata.źródło
Func
zostaną ukryte w nowym wyrażeniu. To po prostu dodaje jedną warstwę danych do kodu; możesz przejść przez jedną warstwę tylko po to, aby znaleźć swój parametrf
bez dalszych szczegółów, więc jesteś od punktu wyjścia.To, co prawdopodobnie powinieneś zrobić, to odwrócić tę metodę. Weź Expression>, skompiluj i uruchom. Jeśli to się nie powiedzie, masz już wyrażenie do sprawdzenia.
Oczywiście musisz wziąć pod uwagę wpływ tego na wydajność i określić, czy jest to coś, co naprawdę musisz zrobić.
źródło
Możesz jednak przejść w drugą stronę za pomocą metody .Compile () - nie jestem pewien, czy jest to przydatne:
źródło
Jeśli czasami potrzebujesz wyrażenia, a czasami potrzebujesz pełnomocnika, masz dwie opcje:
Expression<...>
wersję i po prostu.Compile().Invoke(...)
ją, jeśli chcesz delegata. Oczywiście to kosztuje.źródło
NJection.LambdaConverter to biblioteka konwertująca delegatów na wyrażenie
źródło
źródło
call.Target
część, która mnie zabijała. Działał przez lata, a potem nagle przestał działać i zaczął narzekać na statyczne / niestatyczne bla bla. W każdym razie dzięki!JB Evain z zespołu Cecil Mono robi pewne postępy, aby to umożliwić
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
źródło
Zmiana
Do
źródło