¿Por qué usarías Expression? > en lugar de Func ?

c# delegates expression-trees lambda

Pregunta

Entiendo lambdas y los delegados de Func y Action . Pero las expresiones me tocan. ¿En qué circunstancias usaría una Expression<Func<T>> lugar de una antigua Func<T> ?

Respuesta aceptada

Cuando quiera tratar las expresiones lambda como árboles de expresión, mire dentro de ellas en lugar de ejecutarlas. Por ejemplo, LINQ to SQL obtiene la expresión y la convierte en la declaración SQL equivalente y la envía al servidor (en lugar de ejecutar la lambda).

Conceptualmente, la Expression<Func<T>> es completamente diferente de Func<T> . Func<T> denota un delegate que es más o menos un puntero a un método y Expression<Func<T>> denota una estructura de datos de árbol para una expresión lambda. Esta estructura de árbol describe lo que hace una expresión lambda en lugar de hacer lo real. Básicamente, contiene datos sobre la composición de expresiones, variables, llamadas a métodos, ... (por ejemplo, contiene información como esta lambda es una constante + algún parámetro). Puede usar esta descripción para convertirla en un método real (con Expression.Compile ) o hacer otras cosas (como el ejemplo de LINQ to SQL) con él. El hecho de tratar a las lambdas como métodos anónimos y árboles de expresión es puramente una cuestión de tiempo de compilación.

Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }

se compilará de manera efectiva a un método IL que no obtiene nada y devuelve 10.

Expression<Func<int>> myExpression = () => 10;

se convertirá a una estructura de datos que describe una expresión que no obtiene parámetros y devuelve el valor 10:

Expresión vs Func imagen más grande

Si bien ambos se ven iguales en tiempo de compilación, lo que genera el compilador es totalmente diferente .


Respuesta experta

LINQ es el ejemplo canónico (por ejemplo, hablar con una base de datos), pero en verdad, cada vez que te importa más expresar qué hacer, en lugar de hacerlo realmente. Por ejemplo, uso este enfoque en la pila RPC de protobuf-net (para evitar la generación de código, etc.), por lo que llama a un método con:

string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));

Esto deconstruye el árbol de expresiones para resolver SomeMethod (y el valor de cada argumento), realiza la llamada RPC, actualiza los argumentos ref / out y devuelve el resultado de la llamada remota. Esto solo es posible a través del árbol de expresiones. Cubro esto más aquí .

Otro ejemplo es cuando se construyen los árboles de expresiones manualmente con el propósito de compilarlos en un lambda, como lo hace el código de los operadores genéricos .



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow