Capisco lambda e i delegati di Func
e Action
. Ma le espressioni mi bloccano. In quali circostanze Expression<Func<T>>
piuttosto che una semplice Func<T>
?
Quando vuoi trattare le espressioni lambda come alberi di espressione e guardarle dentro invece di eseguirle. Ad esempio, LINQ to SQL ottiene l'espressione e la converte nell'istruzione SQL equivalente e la invia al server (anziché eseguire il lambda).
Concettualmente, Expression<Func<T>>
è completamente diversa da Func<T>
. Func<T>
denota un delegate
che è praticamente un puntatore a un metodo e Expression<Func<T>>
denota una struttura di dati dell'albero per un'espressione lambda. Questa struttura ad albero descrive cosa fa un'espressione lambda piuttosto che fare la cosa reale. Praticamente contiene dati sulla composizione di espressioni, variabili, chiamate di metodo, ... (per esempio contiene informazioni come questa lambda è un parametro costante + qualche parametro). È possibile utilizzare questa descrizione per convertirla in un metodo effettivo (con Expression.Compile
) o altre cose (come l'esempio LINQ in SQL) con esso. L'atto di trattare lambda come metodi anonimi e alberi di espressione è puramente una questione di tempo compilativo.
Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
compilerà in modo efficace un metodo IL che non ottiene nulla e restituisce 10.
Expression<Func<int>> myExpression = () => 10;
sarà convertito in una struttura dati che descrive un'espressione che non ottiene parametri e restituisce il valore 10:
Mentre entrambi hanno lo stesso aspetto al momento della compilazione, ciò che genera il compilatore è completamente diverso .
LINQ è l'esempio canonico (ad esempio, parlare con un database), ma in verità, ogni volta che ti interessa di più esprimere cosa fare, piuttosto che farlo effettivamente. Ad esempio, utilizzo questo approccio nello stack RPC di protobuf-net (per evitare la generazione di codice, ecc.), Quindi si chiama un metodo con:
string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));
Ciò decostruisce l'albero delle espressioni per risolvere SomeMethod
(e il valore di ciascun argomento), esegue la chiamata RPC, aggiorna qualsiasi argomento ref
/ out
e restituisce il risultato dalla chiamata remota. Questo è possibile solo tramite l'albero delle espressioni. Lo tratterò di più qui .
Un altro esempio è quando si creano manualmente gli alberi delle espressioni allo scopo di compilare un lambda, come fatto dal codice generico degli operatori .