Expresiones lambda (Guía de programación de C#)Lambda Expressions (C# Programming Guide)

Una expresión lambda es una función anónima que se puede usar para crear tipos delegados o de árbol de expresión .A lambda expression is an anonymous function that you can use to create delegates or expression tree types. Al utilizar expresiones lambda, puede escribir funciones locales que se pueden pasar como argumentos o devolverse como valor de llamadas de función.By using lambda expressions, you can write local functions that can be passed as arguments or returned as the value of function calls. Las expresiones lambda son especialmente útiles para escribir expresiones de consulta LINQ.Lambda expressions are particularly helpful for writing LINQ query expressions.

Para crear una expresión lambda, especifique los parámetros de entrada (si existen) a la izquierda del operador lambda =>y coloque el bloque de expresiones o de instrucciones en el otro lado.To create a lambda expression, you specify input parameters (if any) on the left side of the lambda operator =>, and you put the expression or statement block on the other side. Por ejemplo, la expresión lambda x => x * x especifica un parámetro denominado x y devuelve el valor de x cuadrado.For example, the lambda expression x => x * x specifies a parameter that’s named x and returns the value of x squared. Puede asignar esta expresión a un tipo delegado, como se muestra en el ejemplo siguiente:You can assign this expression to a delegate type, as the following example shows:

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:To create an expression tree type:

using System.Linq.Expressions;  

namespace ConsoleApplication1  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            Expression<del> myET = x => x * x;  
        }  
    }  
}  

El operador => tiene la misma prioridad que la asignación (=) y es asociativa a la derecha (vea la sección "Asociatividad" del artículo Operadores).The => operator has the same precedence as assignment (=) and is right associative (see "Associativity" section of the Operators article).

Las lambdas se usan en consultas LINQLINQ basadas en métodos como argumentos de los métodos de operador de consulta estándar, tales como Where.Lambdas are used in method-based LINQLINQ queries as arguments to standard query operator methods such as Where.

Cuando se utiliza la sintaxis de método para llamar al método Where en la clase Enumerable (como se hace en LINQLINQ to Objects y LINQ to XMLLINQ to XML), el parámetro es un tipo delegado System.Func<T,TResult>.When you use method-based syntax to call the Where method in the Enumerable class (as you do in LINQLINQ to Objects and LINQ to XMLLINQ to XML) the parameter is a delegate type System.Func<T,TResult>. Una expresión lambda constituye la manera más práctica de crear ese delegado.A lambda expression is the most convenient way to create that delegate. Cuando se llama al mismo método en, por ejemplo, la clase System.Linq.Queryable (como se hace en LINQ to SQLLINQ to SQL), el tipo de parámetro es System.Linq.Expressions.Expression<Func>, donde Func es uno de los delegados de Func, que tiene hasta dieciséis parámetros de entrada.When you call the same method in, for example, the System.Linq.Queryable class (as you do in LINQ to SQLLINQ to SQL) then the parameter type is an System.Linq.Expressions.Expression<Func> where Func is any of the Func delegates with up to sixteen input parameters. De nuevo, una expresión lambda constituye una manera muy concisa de construir ese árbol de expresión.Again, a lambda expression is just a very concise way to construct that expression tree. Las lambdas permiten que las llamadas a Where tengan un aspecto similar, aunque, de hecho, el tipo de objeto creado a partir de la lambda sea diferente.The lambdas allow the Where calls to look similar although in fact the type of object created from the lambda is different.

En el ejemplo anterior, observe que la signatura de delegado tiene un parámetro de entrada con tipo implícito inty devuelve un int.In the previous example, notice that the delegate signature has one implicitly-typed input parameter of type int, and returns an 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.The lambda expression can be converted to a delegate of that type because it also has one input parameter (x) and a return value that the compiler can implicitly convert to type int. (La inferencia de tipos se analiza con más detalle en las secciones siguientes). Cuando el delegado se invoca mediante un parámetro de entrada de 5, devuelve un resultado de 25.(Type inference is discussed in more detail in the following sections.) When the delegate is invoked by using an input parameter of 5, it returns a result of 25.

Las lambdas no se permiten en el lado izquierdo del operador is o as.Lambdas are not allowed on the left side of the is or as operator.

Todas las restricciones que se aplican a los métodos anónimos también se aplican a las expresiones lambda.All restrictions that apply to anonymous methods also apply to lambda expressions. Para obtener más información, vea Métodos anónimos.For more information, see Anonymous Methods.

Lambdas de expresiónExpression Lambdas

Una expresión lambda con una expresión en el lado derecho del operador => se denomina lambda de expresión.A lambda expression with an expression on the right side of the => operator is called an expression lambda. Las lambdas de expresión se usan ampliamente en la construcción de árboles de expresión.Expression lambdas are used extensively in the construction of Expression Trees. Una expresión lambda devuelve el resultado de evaluar la condición y tiene la siguiente forma:An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

Los paréntesis solo son opcionales si la lambda tiene un parámetro de entrada; de lo contrario, son obligatorios.The parentheses are optional only if the lambda has one input parameter; otherwise they are required. Dos o más parámetros de entrada se separan por comas y se encierran entre paréntesis:Two or more input parameters are separated by commas enclosed in parentheses:

(x, y) => x == y

A veces, es difícil o imposible que el compilador deduzca los tipos de entrada.Sometimes it is difficult or impossible for the compiler to infer the input types. Cuando ocurre esto, los tipos se pueden especificar explícitamente como se muestra en el ejemplo siguiente:When this occurs, you can specify the types explicitly as shown in the following example:

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

Para especificar cero parámetros de entrada, utilice paréntesis vacíos:Specify zero input parameters with empty parentheses:

() => SomeMethod()

Observe en el ejemplo anterior que el cuerpo de una lambda de expresión puede estar compuesto de una llamada a método.Note in the previous example that the body of an expression lambda can consist of a method call. Sin embargo, si crea árboles de expresiones que se evalúan fuera de .NET Framework, por ejemplo en SQL Server, no debe utilizar llamadas a métodos en expresiones lambda.However, if you are creating expression trees that are evaluated outside of the .NET Framework, such as in SQL Server, you should not use method calls in lambda expressions. Los métodos no tendrán ningún significado fuera del contexto de .NET Common Language Runtime.The methods will have no meaning outside the context of the .NET common language runtime.

Lambdas de instrucciónStatement Lambdas

Una lambda de instrucción es similar a un lambda de expresión, salvo que las instrucciones se encierran entre llaves:A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(parámetros de entrada) => { instrucción; }(input-parameters) => { statement; }

El cuerpo de una lambda de instrucción puede estar compuesto de cualquier número de instrucciones; sin embargo, en la práctica, generalmente no hay más de dos o tres.The body of a statement lambda can consist of any number of statements; however, in practice there are typically no more than two or three.

delegate void TestDelegate(string s);
TestDelegate del = n => { string s = n + " World"; 
                          Console.WriteLine(s); };

Las lambdas de instrucción, como los métodos anónimos, no se pueden utilizar para crear árboles de expresión.Statement lambdas, like anonymous methods, cannot be used to create expression trees.

Lambdas asincrónicasAsync Lambdas

Puede crear fácilmente expresiones e instrucciones lambda que incorporen el procesamiento asincrónico mediante las palabras clave async y await .You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. Por ejemplo, en el siguiente ejemplo de formularios Windows Forms se incluye un controlador de eventos que llama y espera un método asincrónico, ExampleMethodAsync.For example, the following Windows Forms example contains an event handler that calls and awaits an async method, ExampleMethodAsync.

public partial class Form1 : Form  
{  
    public Form1()  
    {  
        InitializeComponent();  
    }  

    private async void button1_Click(object sender, EventArgs e)  
    {  
        // ExampleMethodAsync returns a Task.  
        await ExampleMethodAsync();  
        textBox1.Text += "\r\nControl returned to Click event handler.\n";  
    }  

    async Task ExampleMethodAsync()  
    {  
        // The following line simulates a task-returning asynchronous process.  
        await Task.Delay(1000);  
    }  
}  

Puede agregar el mismo controlador de eventos utilizando una lambda asincrónica.You can add the same event handler by using an async lambda. Para agregar este controlador, agregue un modificador async antes de la lista de parámetros lambda, como se muestra en el ejemplo siguiente.To add this handler, add an async modifier before the lambda parameter list, as the following example shows.

public partial class Form1 : Form  
{  
    public Form1()  
    {  
        InitializeComponent();  
        button1.Click += async (sender, e) =>  
        {  
            // ExampleMethodAsync returns a Task.  
            await ExampleMethodAsync();  
            textBox1.Text += "\nControl returned to Click event handler.\n";  
        };  
    }  

    async Task ExampleMethodAsync()  
    {  
        // The following line simulates a task-returning asynchronous process.  
        await Task.Delay(1000);  
    }  
}  

Para obtener más información sobre cómo crear y usar métodos asincrónicos, vea Programación asincrónica con async y await.For more information about how to create and use async methods, see Asynchronous Programming with async and await.

Lambdas con los operadores de consulta estándarLambdas with the Standard Query Operators

Muchos operadores de consulta estándar tienen un parámetro de entrada cuyo tipo es uno de la familia Func<T,TResult> de delegados genéricos.Many Standard query operators have an input parameter whose type is one of the Func<T,TResult> family of generic delegates. Estos delegados usan parámetros de tipo para definir el número y los tipos de los parámetros de entrada y el tipo de valor devuelto del delegado.These delegates use type parameters to define the number and types of input parameters, and the return type of the delegate. Los delegadosFunc son muy útiles para encapsular expresiones definidas por el usuario que se aplican a cada elemento en un conjunto de datos de origen.Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. Por ejemplo, considere el siguiente tipo delegado:For example, consider the following delegate type:

public delegate TResult Func<TArg0, TResult>(TArg0 arg0)  

Se pueden crear instancias del delegado como Func<int,bool> myFunc , donde int es un parámetro de entrada y bool es el valor devuelto.The delegate can be instantiated as Func<int,bool> myFunc where int is an input parameter and bool is the return value. El valor devuelto siempre se especifica en el último parámetro de tipo.The return value is always specified in the last type parameter. Func<int, string, bool> define un delegado con dos parámetros de entrada, int y string, y un tipo de valor devuelto de bool.Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. El siguiente delegado Func , cuando se invoca, devolverá true o false para indicar si el parámetro de entrada es igual a 5:The following Func delegate, when it is invoked, will return true or false to indicate whether the input parameter is equal to 5:

Func<int, bool> 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<Func>, por ejemplo, en los operadores de consulta estándar que se definen en System.Linq.Queryable.You can also supply a lambda expression when the argument type is an Expression<Func>, for example in the standard query operators that are defined in System.Linq.Queryable. Al especificar un argumento Expression<Func> , la lambda se compilará en un árbol de expresión.When you specify an Expression<Func> argument, the lambda will be compiled to an expression tree.

A continuación, se muestra un operador de consulta estándar, el método Count :A standard query operator, the Count method, is shown here:

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.The compiler can infer the type of the input parameter, or you can also specify it explicitly. Esta expresión lambda particular cuenta aquellos enteros (n) que divididos por dos dan como resto 1.This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

La siguiente línea de código produce una secuencia que contiene todos los elementos de la matriz numbers que aparecen a la izquierda del 9, ya que ese es el primer número de la secuencia que no cumple la condición:The following line of code produces a sequence that contains all elements in the numbers array that are to the left side of the 9 because that's the first number in the sequence that doesn't meet the condition:

var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);  

En este ejemplo se muestra cómo especificar varios parámetros de entrada encerrándolos entre paréntesis.This example shows how to specify multiple input parameters by enclosing them in parentheses. 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.The method returns all the elements in the numbers array until a number is encountered whose value is less than its position. No confunda el operador lambda (=>) con el operador mayor o igual que (>=).Do not confuse the lambda operator (=>) with the greater than or equal operator (>=).

var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);  

Inferencia de tipos en las lambdasType Inference in Lambdas

Al escribir lambdas, 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 lambda, el tipo de delegado del parámetro y otros factores que se describen en la especificación del lenguaje C#.When writing lambdas, you often do not have to specify a type for the input parameters because the compiler can infer the type based on the lambda body, the parameter’s delegate type, and other factors as described in the C# Language Specification. 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.For most of the standard query operators, the first input is the type of the elements in the source sequence. Por consiguiente, si está realizando una consulta sobre un IEnumerable<Customer>, 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:So if you are querying an IEnumerable<Customer>, then the input variable is inferred to be a Customer object, which means you have access to its methods and properties:

customers.Where(c => c.City == "London");  

Las reglas generales para las lambdas son las siguientes:The general rules for lambdas are as follows:

  • La lambda debe contener el mismo número de parámetros que el tipo delegado.The lambda must contain the same number of parameters as the delegate type.

  • Cada parámetro de entrada de la lambda debe poder convertirse implícitamente a su parámetro de delegado correspondiente.Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • El valor devuelto de la lambda (si existe) debe poder convertirse implícitamente al tipo de valor devuelto del delegado.The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

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".Note that lambda expressions in themselves do not have a type because the common type system has no intrinsic concept of "lambda expression." Sin embargo, a veces resulta práctico hablar coloquialmente del "tipo" de una expresión lambda.However, it is sometimes convenient to speak informally of the "type" of a lambda expression. 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.In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Ámbito de las variables en las expresiones lambdaVariable Scope in Lambda Expressions

Las lambdas pueden hacer referencia a las variables externas (vea Métodos anónimos) que están en el ámbito del método que define la función lambda o en el ámbito del tipo que contiene la expresión lambda.Lambdas can refer to outer variables (see Anonymous Methods) that are in scope in the method that defines the lambda function, or in scope in the type that contains the lambda expression. 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 la recolección de elementos no utilizados.Variables that are captured in this manner are stored for use in the lambda expression even if the variables would otherwise go out of scope and be garbage collected. Para poder utilizar una variable externa en una expresión lambda, debe estar previamente asignada.An outer variable must be definitely assigned before it can be consumed in a lambda expression. En el ejemplo siguiente se muestran estas reglas:The following example demonstrates these rules:

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 las variables en las expresiones lambda:The following rules apply to variable scope in lambda expressions:

  • Una variable capturada no se recolectará como elemento no utilizado hasta que el delegado que hace referencia a ella sea elegible para la recolección de elementos no utilizados.A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • Las variables introducidas en una expresión lambda no son visibles en el método externo.Variables introduced within a lambda expression are not visible in the outer method.

  • Una expresión lambda no puede capturar directamente un parámetro in, ref o out desde un método que la englobe.A lambda expression cannot directly capture an in, ref, or out parameter from an enclosing method.

  • Una instrucción return en una expresión lambda no hace que el método que la engloba devuelva un valor.A return statement in a lambda expression does not cause the enclosing method to return.

  • Una expresión lambda no puede contener una instrucción goto , break o continue que esté dentro de la función lambda si el destino de la instrucción de salto está fuera del bloque.A lambda expression cannot contain a goto statement, break statement, or continue statement that is inside the lambda function if the jump statement’s target is outside the block. También es un error utilizar una instrucción de salto fuera del bloque de la función lambda si el destino está dentro del bloque.It is also an error to have a jump statement outside the lambda function block if the target is inside the block.

Especificación del lenguaje C#C# Language Specification

Para obtener más información, consulte la Especificación del lenguaje C#.For more information, see the C# Language Specification. La especificación del lenguaje es la fuente definitiva de la sintaxis y el uso de C#.The language specification is the definitive source for C# syntax and usage.

Delegates, Events, and Lambda Expressions (Delegados, eventos y expresiones lambda) en C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmersDelegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Vea tambiénSee Also

Guía de programación de C#C# Programming Guide
LINQ (Language Integrated Query)LINQ (Language-Integrated Query)
Métodos anónimosAnonymous Methods
isis
Árboles de expresiónExpression Trees
Ejemplos de C# de Visual Studio 2008 (vea los archivos de ejemplos de consultas LINQ y el programa XQuery)Visual Studio 2008 C# Samples (see LINQ Sample Queries files and XQuery program)
Expresiones lambda recursivasRecursive lambda expressions