Ir de um lambda para uma Expressão é fácil usando uma chamada de método ...
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
Mas eu gostaria de transformar o Func em uma expressão, apenas em casos raros ...
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());
}
A linha que não funciona me dá o erro de tempo de compilação Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'
. Um elenco explícito não resolve a situação. Existe uma facilidade para fazer isso que estou esquecendo?
at lambda_method(Closure )
a invocação do delegado compilado.Respostas:
Ooh, não é nada fácil.
Func<T>
representa um genéricodelegate
e não uma expressão. Se houver alguma maneira de fazer isso (devido a otimizações e outras coisas feitas pelo compilador, alguns dados podem ser jogados fora, então pode ser impossível obter a expressão original de volta), seria desmontar o IL na hora e inferir a expressão (o que não é nada fácil). Tratar expressões lambda como data (Expression<Func<T>>
) é uma mágica feita pelo compilador (basicamente, o compilador constrói uma árvore de expressão no código em vez de compilá-la para IL).Fato relacionado
É por isso que as linguagens que levam lambdas ao extremo (como Lisp) são geralmente mais fáceis de implementar como interpretadores . Nessas linguagens, código e dados são essencialmente a mesma coisa (mesmo em tempo de execução ), mas nosso chip não consegue entender essa forma de código, então temos que emular tal máquina construindo um interpretador em cima dela que a entenda (o escolha feita por linguagens como o Lisp) ou sacrificando o poder (o código não será mais exatamente igual aos dados) até certo ponto (a escolha feita pelo C #). No C #, o compilador dá a ilusão de tratar o código como dados, permitindo que lambdas sejam interpretados como code (
Func<T>
) e data (Expression<Func<T>>
) no momento da compilação .fonte
eval
, precisará inicializar o compilador, mas além disso, não há problema nenhum em fazer isso.Expression
sobre sua ação de wrapper, mas não teria nenhuma informação de árvore de expressão sobre os internos dodangerousCall
delegado.fonte
Func
ficará oculto em uma nova Expressão. Isso simplesmente adiciona uma camada de dados sobre o código; você pode atravessar uma camada apenas para encontrar seu parâmetrof
sem mais detalhes, então você está exatamente onde começou.O que você provavelmente deve fazer é inverter o método. Pegue um Expression>, compile e execute. Se falhar, você já tem a Expressão para examinar.
Obviamente, você precisa considerar as implicações disso no desempenho e determinar se é algo que você realmente precisa fazer.
fonte
No entanto, você pode fazer o contrário por meio do método .Compile () - não tenho certeza se isso é útil para você:
fonte
Se às vezes você precisa de uma expressão e às vezes de um delegado, você tem 2 opções:
Expression<...>
versão, e apenas.Compile().Invoke(...)
se quiser um delegado. Obviamente, isso tem um custo.fonte
NJection.LambdaConverter é uma biblioteca que converte delegados em expressão
fonte
fonte
call.Target
parte que estava me matando. Funcionou por anos e, de repente, parou de funcionar e começou a reclamar de um blá blá estático / não estático. Enfim, obrigado!JB Evain, da equipe Cecil Mono, está fazendo alguns progressos para habilitar este
http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees
fonte
mudança
Para
fonte