Professional Documents
Culture Documents
19/06/2016
Tiempo de lectura: 9 minutos
Colaboradores
o
var sum = 1 + 2;
Esto puede parecer complicado, pero resulta muy eficaz. Siguiendo el mismo
proceso, puede descomponer expresiones mucho más complicadas. Tomemos
esta expresión como ejemplo:
C#Copiar
Una vez que esté familiarizado con la estructura de los árboles de expresiones,
verá que los conocimientos que ha adquirido le permiten trabajar rápidamente
con muchos escenarios más avanzados. Los árboles de expresiones ofrecen
posibilidades increíbles.
Las API de los árboles de expresiones permiten crear árboles que representan
casi cualquier construcción de código válida. En cambio, para que todo resulte
lo más sencillo posible, algunas expresiones de C# no se pueden crear en un
árbol de expresión. Un ejemplo son las expresiones asincrónicas (mediante las
palabras clave async y await ). Si necesita algoritmos asincrónicos, tendría que
manipular los objetos Task directamente, en lugar de confiar en la
compatibilidad del compilador. Otro ejemplo es en la creación de
bucles. Normalmente, puede crearlos usando
bucles for , foreach , while o do . Como verá más adelante en esta serie, las
API de los árboles de expresiones admiten una expresión de bucle individual,
con expresiones break y continue que controlan la repetición del bucle.
o
o
Hay una amplia lista de clases en .NET Core Framework que funcionan con
árboles de expresiones.En System.Linq.Expressions puede ver la lista
completa. En lugar de analizarla al completo, vamos a explicar cómo se han
diseñado las clases del marco.
Por ejemplo, este código imprimirá el nombre de una variable para una
expresión de acceso a la variable. He seguido el procedimiento que consiste en
comprobar el tipo de nodo, convertirlo en una expresión de acceso a la variable
y después comprobar las propiedades del tipo de expresión específico:
C#Copiar
if (addFive.NodeType == ExpressionType.Lambda)
{
var lambdaExp = (LambdaExpression)addFive;
Console.WriteLine(parameter.Name);
Console.WriteLine(parameter.Type);
}
En este sencillo ejemplo puede ver que hay muchos tipos implicados a la hora
de crear árboles de expresiones y trabajar con ellos. Esta complejidad resulta
necesaria para proporcionar las capacidades del vocabulario variado que ofrece
el lenguaje C#.
Navegar por las API
Hay tipos de nodos de expresión que se asignan a casi todos los elementos de
sintaxis del lenguaje C#. Cada tipo tiene métodos específicos para ese tipo de
elemento del lenguaje. Es mucha información como para recordarla toda. En
lugar de intentar memorizar todo, estas son las técnicas que uso para trabajar
con árboles de expresiones:
Encontrará más información a medida que observe cada una de esas tres
áreas. Siempre encontrará lo que necesita empezando con uno de esos tres
pasos.
o
Anterior: Tipos de marco que admiten árboles de expresión
En la mayoría de los casos, esto crea una asignación simple entre una expresión
y su delegado correspondiente. Por ejemplo, un árbol de expresión que se
representa por Expression<Func<int>> se convertiría a un delegado del
tipo Func<int> . Para una expresión lambda con cualquier tipo de valor
devuelto y lista de argumentos, existe un tipo de delegado que es el tipo de
destino para el código ejecutable representado por esa expresión lambda.
Las expresiones lambda crean clausuras sobre las variables locales a las que se
hace referencia en la expresión. Debe garantizar que las variables que formarían
parte del delegado se pueden usar en la ubicación desde la que se llama
a Compile , y cuando se ejecuta el delegado resultante.
Hay una gran cantidad de permutaciones de este problema, por lo que resulta
difícil ofrecer instrucciones generales para evitarlo. Tenga cuidado al obtener
acceso a las variables locales al definir expresiones y al obtener acceso al estado
en el objeto actual (representado por this ) al crear un árbol de expresión que
pueda ser devuelto por una API pública.
Interpretación de expresiones
19/06/2016
Tiempo de lectura: 20 minutos
Colaboradores
o
o
o
Previous -- Executing Expressions (Anterior: Ejecución de expresiones)
Ese diseño hace que la visita de todos los nodos de un árbol de expresión sea
una operación recursiva relativamente sencilla. La estrategia general es
comenzar en el nodo raíz y determinar qué tipo de nodo es.
// Lambda Visitor
public class LambdaVisitor : Visitor
{
private readonly LambdaExpression node;
public LambdaVisitor(LambdaExpression node) : base(node)
{
this.node = node;
}
// Parameter visitor:
public class ParameterVisitor : Visitor
{
private readonly ParameterExpression node;
public ParameterVisitor(ParameterExpression node) : base(node)
{
this.node = node;
}
// Constant visitor:
public class ConstantVisitor : Visitor
{
private readonly ConstantExpression node;
public ConstantVisitor(ConstantExpression node) : base(node)
{
this.node = node;
}
Si ejecuta esta expresión a través del visitante, verá este resultado y comprobará
que la expresión de adición simple es asociativa por la izquierda.
Para ejecutar este ejemplo, y ver el árbol de expresión completo, tuve que
realizar un cambio en el árbol de expresión de origen. Cuando el árbol de
expresión contiene todas las constantes, el árbol resultante simplemente
contiene el valor constante de 10 . El compilador realiza toda la adición y
reduce la expresión a su forma más simple. Simplemente con agregar una
variable a la expresión es suficiente para ver el árbol original:
C#Copiar
Expression<Func<int, int>> sum = (a) => 1 + a + 3 + 4;
Cree un visitante para esta suma y ejecute el visitante para ver este resultado:
Copiar
También puede ejecutar cualquiera de los otros ejemplos a través del código de
visitante y ver qué árbol representa. Aquí se muestra un ejemplo de la
expresión sum3 anterior (con un parámetro adicional para evitar que el
compilador calcule la constante):
C#Copiar
Tenga en cuenta que los paréntesis no forman parte del resultado. No existen
nodos en el árbol de expresión que representen los paréntesis en la expresión
de entrada. La estructura del árbol de expresión contiene toda la información
necesaria para comunicar la precedencia.
Ampliar a partir de este ejemplo
El ejemplo trata solo los árboles de expresión más básicos. El código que ha
visto en esta sección solo controla enteros constantes y el operador
binario + . Como último ejemplo, vamos a actualizar el visitante para que
controle una expresión más complicada. Hagamos que funcione para esto:
C#Copiar
Los ejemplos de esta sección muestran las técnicas principales para visitar y
examinar nodos de un árbol de expresión. He pasado por alto muchas acciones
que puede necesitar para concentrarme en las tareas principales a la hora de
visitar y acceder a los nodos de un árbol de expresión.
En primer lugar, los visitantes solo controlan constantes que son enteros. Los
valores constantes pueden ser cualquier otro tipo numérico, y el lenguaje de C#
admite conversiones y promociones entre esos tipos. Una versión más sólida de
este código reflejará todas esas capacidades.
Por último, la biblioteca que he usado en este artículo se ha creado con fines de
demostración y aprendizaje. No está optimizada. La he escrito para dejar claro
las estructuras que he usado y para resaltar las técnicas usadas para visitar los
nodos y analizar lo que hay ahí. Una implementación de producción prestaría
más atención al rendimiento.
o
Previous -- Interpreting Expressions (Anterior: Interpretación de expresiones)
Hasta ahora, todos los árboles de expresión que ha visto se han creado con el
compilador de C#.Todo lo que tenía que hacer era crear una expresión lambda
que se asignaba a una variable de tipo Expression<Func<T>> o de algún tipo
similar. Esa no es la única manera de crear un árbol de expresión. En muchos
escenarios, puede que necesite crear una expresión en memoria en tiempo de
ejecución.
Para crear ese árbol de expresión, debe crear los nodos de hoja. Los nodos de
hoja son constantes, por lo que puede usar el
método Expression.Constant para crear los nodos:
C#Copiar
Una vez que tenga la expresión de adición, puede crear la expresión lambda:
C#Copiar
Para las expresiones que son tan simples como esta, puede combinar todas las
llamadas en una sola instrucción:
C#Copiar
Crear un árbol
En este ejemplo más complejo, verá un par de técnicas más que necesitará a
menudo para crear árboles de expresión.
No está limitado en lo que puede crear con estas API. En cambio, cuánto más
complicado sea el árbol de expresión que quiera crear, más difícil será la
administración y la lectura del código.
En esta sección, también he actualizado el código del visitante para visitar cada
nodo de este árbol de expresión y escribir información sobre los nodos que se
crean en este ejemplo. Puede ver o descargar el código de ejemplo en el
repositorio dotnet/docs de GitHub. Pruébelo compilando y ejecutando los
ejemplos. Para obtener instrucciones de descarga, vea Ejemplos y tutoriales.
Examinar las API
Las API del árbol de expresión son algunas de las más difíciles para navegar en
.NET Core, pero no pasa nada. Su finalidad es una tarea más compleja: escribir
código que genere código en tiempo de ejecución. Son inevitablemente
complicadas a la hora de proporcionar un equilibrio entre la compatibilidad con
todas las estructuras de control disponibles en el lenguaje de C# y mantener el
área expuesta de las API tan pequeña como sea razonable. Este equilibrio
significa que muchas estructuras de control se representan no por sus
construcciones de C#, sino por construcciones que representan la lógica
subyacente que genera el compilador desde estas construcciones de nivel
superior.
https://docs.microsoft.com/es-es/dotnet/csharp/expression-trees-building