Expresiones Lambda C#

Expresiones Lambda C# Una expresión lambda es una función anónima que se puede utilizar para crear tipos delegados o árb

Views 175 Downloads 0 File size 371KB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

Expresiones Lambda C# Una expresión lambda es una función anónima que se puede utilizar para crear tipos delegados o árbol de expresión . Al utilizar expresiones lambda, puede escribir funciones locales que se pueden pasar como argumentos o devolver como valor de llamadas de función. Las expresiones lambda son especialmente útiles para escribir expresiones de consulta de LINQ. Para crear una expresión lambda, especifique parámetros de entrada (si existen) a la izquierda del operador =>lambda, y coloca la expresión o bloque de instrucciones en el otro lado. Por ejemplo, la expresión lambda x => x * x especifica un parámetro denominado x y devuelve el valor x elevado al cuadrado. Puede asignar esta expresión a un tipo delegado, como se muestra en el ejemplo siguiente:

delegate int del(int i); static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 } Para crear un tipo de árbol de expresión: using System.Linq.Expressions; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Expression myET = x => x * x; } } } El operador => tiene la misma prioridad que la asignación (=) y es asociativo por la derecha. Las expresiones lambda se usan en consultas de LINQ basadas en métodos como argumentos de los métodos de operador de consulta estándar, tales como Where. Cuando se utiliza la sintaxis de método para llamar al método Where en la clase Enumerable (como se hace en LINQ to Objects y LINQ to XML), el parámetro es un tipo delegado System.Func. Una expresión lambda constituye la manera más práctica de crear ese delegado. Cuando se llama al mismo método en, por ejemplo, la clase System.Linq.Queryable (como se hace en LINQ to SQL), el tipo de parámetro es System.Linq.Expressions.Expression, donde Func es cualquier delegado de Func que tenga hasta dieciséis parámetros de entrada. De nuevo, una expresión lambda constituye una manera muy concisa de construir ese árbol de expresión. Las expresiones lambda permiten que las llamadas a Where tengan un aspecto similar, aunque, de hecho, el tipo de objeto creado desde la expresión lambda sea diferente. En el ejemplo anterior, observe que la firma de delegado tiene un parámetro de entrada con tipo implícito int y devuelve un int. La expresión lambda se puede convertir en un delegado de ese tipo porque también tiene un parámetro de entrada (x) y un valor devuelto que el compilador puede convertir implícitamente al tipo int. (La inferencia de tipo se analiza con más detalle en las siguientes secciones.) Cuando el delegado se invoca utilizando un parámetro de entrada de 5, devuelve un resultado de 25. Las expresiones lambda no se permiten en el lado izquierdo del operador is o as. Todas las restricciones que se aplican a los métodos anónimos también se aplican a las expresiones lambda. Para obtener más información, vea Métodos anónimos (Guía de programación de C#).

Lambdas de expresión

Una expresión lambda con una expresión en el lado derecho se denomina lambda de expresión. Las lambdas de expresión se utilizan ampliamente en la construcción de Árboles de expresión (C# y Visual Basic). Una lambda de expresión devuelve el resultado de la expresión y presenta la siguiente forma básica: (input parameters) => expression 

Los paréntesis sólo son opcionales si la lambda tiene un parámetro de entrada; de lo contrario, son obligatorios. Dos o más parámetros de entrada se separan por comas y se encierran entre paréntesis:

(x, y) => x == y 

A veces, es difícil o imposible para el compilador deducir los tipos de entrada. Cuando esto ocurre, puede especificar los tipos explícitamente como se muestra en el ejemplo siguiente:

(int x, string s) => s.Length > x 

Para especificar cero parámetros de entrada, utilice paréntesis vacíos:

() => SomeMethod() Observe en el ejemplo anterior que el cuerpo de una lambda de expresión puede estar compuesto de una llamada a método. Sin embargo, si va a crear árboles de expresión que se utilizarán en otro dominio, como SQL Server, no debería utilizar llamadas a métodos en expresiones lambda. Los métodos no tendrán ningún significado fuera del contexto de .NET Common Language Runtime.

Lambdas de instrucciones Una lambda de instrucciones es similar a un lambda de expresión, salvo que las instrucciones se encierran entre llaves: (input parameters) => {statement;} El cuerpo de una instrucción lambda puede estar compuesto de cualquier número de instrucciones; sin embargo, en la práctica, generalmente no hay más de dos o tres. C# delegate void TestDelegate(string s); … TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); }; myDel("Hello"); Las lambdas de instrucciones, como los métodos anónimos, no se pueden utilizar para crear árboles de expresión.

Lambdas con los operadores de consulta estándar Muchos operadores de consulta estándar tienen un parámetro de entrada cuyo tipo es uno de la familia Func de delegados genéricos. Los delegados de Func usan parámetros de tipo para definir el número y el tipo de los parámetros de entrada y el tipo de valor devuelto del delegado. Los delegados Func son muy útiles para encapsular expresiones definidas por el usuario que se aplican a cada elemento en un conjunto de datos de origen. Por ejemplo, considere el siguiente tipo delegado: C# public delegate TResult Func(TArg0 arg0) Se pueden crear instancias del delegado como Func myFunc, donde int es un parámetro de entrada y bool es el valor devuelto. El valor devuelto siempre se especifica en el último parámetro de tipo.Func define un delegado con dos parámetros de entrada, int y string, y un tipo de valor devuelto de bool. El siguiente delegado Func, cuando se invoca, devolverá verdadero o falso para indicar si el parámetro de entrada es igual a 5: C# Func myFunc = x => x == 5; bool result = myFunc(4); // returns false of course También puede proporcionar una expresión lambda cuando el tipo de argumento es Expression, por ejemplo, en los operadores de consulta estándar que se definen en System.Linq.Queryable. Al especificar un argumento Expression, la expresión lambda se compilará en un árbol de expresión. A continuación, se muestra un operador de consulta estándar, el método Count: C# int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; int oddNumbers = numbers.Count(n => n % 2 == 1); El compilador puede deducir el tipo del parámetro de entrada, o también se puede especificar explícitamente. Esta expresión lambda particular cuenta aquellos enteros (n) que divididos por dos dan como resto 1. El método siguiente generará una secuencia que contiene todos los elementos de la matriz numbers que aparecen en la parte izquierda del 9, ya que ese es el primer número de la secuencia que no cumple la condición: C# var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6); Este ejemplo muestra cómo especificar varios parámetros de entrada encerrándolos entre paréntesis. El método devuelve todos los elementos de la matriz de números hasta encontrar un número cuyo valor sea menor que su posición. No confunda el operador lambda (=>) con el operador mayor o igual que (>=). C# var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);

Deducción de tipos en las expresiones lambda Al escribir expresiones lambda, no tiene por qué especificar un tipo para los parámetros de entrada, ya que el compilador puede deducir el tipo según el cuerpo de la lambda, el tipo de delegado subyacente y otros factores que se describen en la especificación del lenguaje C#. Para la mayoría de los operadores de consulta estándar, la primera entrada es el tipo de los elementos en la secuencia de origen. Así, si está realizando una consulta sobre un IEnumerable, se deducirá que la variable de entrada será un objeto Customer, lo cual significa que se podrá tener acceso a sus métodos y propiedades: C# customers.Where(c => c.City == "London"); Las reglas generales para las expresiones lambda son las siguientes:  La lambda debe contener el mismo número de parámetros que el tipo delegado.  Cada parámetro de entrada en la lambda se debe poder convertir implícitamente a su parámetro de delegado correspondiente.  El valor devuelto de la lambda (si existe) se debe poder convertir implícitamente al tipo de valor devuelto del delegado. Observe que las expresiones lambda, en sí mismas, no tienen tipo, ya que el sistema de tipos comunes no tiene ningún concepto intrínseco de "expresión lambda". Sin embargo, a veces resulta práctico hablar informalmente del "tipo" de una expresión lambda. En estos casos, el tipo hace referencia al tipo del delegado o el tipo de Expression en el que se convierte la expresión lambda.

Ámbito de las variables en expresiones lambda Las expresiones lambda pueden hacer referencia a variables externas que se encuentran dentro del ámbito del método o tipo que las engloba y en el cual se definen. Las variables que se capturan de esta manera se almacenan para su uso en la

expresión lambda, cuando de otro modo quedarían fuera de ámbito y serían eliminadas por el recolector de elementos no utilizados. Para poder utilizar una variable externa en una expresión lambda, debe estar previamente asignada. En el ejemplo siguiente se muestran estas reglas. delegate bool D(); delegate bool D2(int i); class Test { D del; D2 del2; public void TestMethod(int input) { int j = 0; // Initialize the delegates with lambda expressions. // Note access to 2 outer variables. // del will be invoked within this method. del = () => { j = 10; return j > input; }; // del2 will be invoked after TestMethod goes out of scope. del2 = (x) => {return x == j; }; // Demonstrate value of j: // Output: j = 0 // The delegate has not been invoked yet. Console.WriteLine("j = {0}", j); // Invoke the delegate. bool boolResult = del(); // Output: j = 10 b = True Console.WriteLine("j = {0}. b = {1}", j, boolResult); } static void Main() { Test test = new Test(); test.TestMethod(5); // Prove that del2 still has a copy of // local variable j from TestMethod. bool result = test.del2(10); // Output: True Console.WriteLine(result); Console.ReadKey(); } } Las reglas siguientes se aplican al ámbito de variables en expresiones lambda:  Una variable que se captura no será eliminada por el recolector de elementos no utilizados hasta que el delegado que hace referencia a ella quede fuera del ámbito.  Las variables introducidas dentro de una expresión lambda no son visibles en el método externo.  Una expresión lambda no puede capturar directamente un parámetro ref o out desde un método que la englobe.  Una instrucción return en una expresión lambda no hace que el método que la engloba vuelva.  Una expresión lambda no puede contener una instrucción goto, una instrucción break o una instrucción continue cuyo destino esté fuera del cuerpo o en el cuerpo de una función anónima contenida.

C# Expresiones Lambda Veamos ejemplos prácticos y sencillos del uso de expresiones lambda, de forma que nos quede claro su concepto y su uso. ¿Que es una expresión lambda?

"Una expresión lambda es una función anónima que puede contener expresiones e instrucciones y se puede utilizar para crear delegados o tipos de árboles de expresión". Una forma más simple de definirla sería: 

Para definir una expresión lambda se usa el llamado operador lambda (=>).



A la izquierda de este operador se indicarán las variables o parámetros de la función anónima.



A la derecha del operador indicaremos el código de la función (la expresión o bloque de instrucciones).

Hasta aquí la definición más sencilla posible, pero igualmente algo complicada de entender, expliquémosla a través de ejemplos abstractos con la esperanza de que se comprenda mejor: Ejemplo Básico de Expresión Lambda

delegate int SumaDeledado(int a, int b); SumaDeledado SumaFuncion = (a, b) => a + b; Ejemplos de Expresiones Lambda en LINQ

El principal uso de las expresiones lambda está vinculado con las expresiones de consultas LINQ. Así que veamos algunos ejemplos de estas expresiones, estos ejemplos son bien sencillos pero nos vienen bien para comprender el uso de las expresiones lambda. int[] nums = { 3, 4, 5, 6, 4, 5, 7 }; var numMayores = nums.Where(n => n > 5).ToList(); var numPares = nums.Where(n => n % 2 == 0).ToList(); var numImpares = nums.Where(n => n % 2 != 0).ToList();

El en código de ejemplo anterior tenemos 3 consultas LINQ:  La primera consulta la usamos para guardar en la variable de tipo anónimo (numMayores) los números que sean mayores de 5, y para lograrlo usamos la expresión lambda (n => n > 5). El resultado sería evidentemente { 6, 7 }.  En la segunda consulta (numPares) obtendremos los números pares usando la siguiente expresión lambda(n => n % 2 == 0). El resultado sería evidentemente { 4, 6, 4 }.  En la tercera consulta (numImpares) obtendremos los números impares usando la siguiente expresión lambda (n => n % 2 != 0).

Expresiones Lambda Consideraciones Condiciones que debemos tener en cuenta a la hora de definir una expresión lambda: 

Las expresiones lambda se definen usando el operador lambda =>.

 Para devolver el valor no se utiliza la instrucción return, (salvo que el contenido de la expresión lambda sea un bloque encerrado entre llaves).  Los tipos de los parámetros de las expresiones lambda siempre se infieren, ya que siempre habrá algún delegado asociado con el uso de la expresión lambda. Si se indica el tipo de uno de los parámetros, debemos indicarlos todos, independientemente de que se pueda inferir el tipo. 

Los parámetros de la expresión lambda no pueden ser opcionales, por tanto, no podemos usar params.



Los parámetros no pueden ser de tipo ref u out.



No podemos indicar parámetros generic.

 Los nombres de los parámetros no pueden ser los mismos que los de otros elementos que estén en el mismo ámbito. 

Los parámetros de una expresión lambda son solo visibles dentro de esa función.

 Las expresión lambda las podemos usar para cualquier tipo de delegado, ya sean definidos por nosotros o los definidos por el propio .NET. Y hasta aquí esta charla sobre expresiones lambda, espero te halla servido para comprender un poco mejor esta interesante característica.