Expressões lambda (Guia de Programação em C#)Lambda Expressions (C# Programming Guide)

Uma expressão lambda é uma função anônima que você pode usar para criar delegados ou tipos de árvore de expressão.A lambda expression is an anonymous function that you can use to create delegates or expression tree types. Ao usar expressões lambda, você pode escrever funções locais que podem ser passadas como argumentos ou retornadas como o valor de chamadas de função.By using lambda expressions, you can write local functions that can be passed as arguments or returned as the value of function calls. Expressões lambda são particularmente úteis para escrever expressões de consulta LINQ.Lambda expressions are particularly helpful for writing LINQ query expressions.

Para criar uma expressão lambda, especifique os parâmetros de entrada (se houver) no lado esquerdo do operador lambda => e coloque a expressão ou o bloco de instruções do outro 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 exemplo, a expressão lambda x => x * x especifica um parâmetro chamado x e retorna o valor de x ao quadrado.For example, the lambda expression x => x * x specifies a parameter that’s named x and returns the value of x squared. Você pode atribuir essa expressão a um tipo delegado como neste exemplo: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 criar um tipo de árvore de expressão: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;  
        }  
    }  
}  

O operador => tem a mesma precedência que a atribuição (=) e é associativo direito (consulte a seção "Associatividade" do artigo Operadores).The => operator has the same precedence as assignment (=) and is right associative (see "Associativity" section of the Operators article).

Lambdas são usadas em consultas LINQLINQ baseadas em métodos como argumentos para métodos de operador de consulta padrão como Where.Lambdas are used in method-based LINQLINQ queries as arguments to standard query operator methods such as Where.

Quando você usa a sintaxe baseada em método para chamar o método Where na classe Enumerable (assim como você faz em LINQLINQ para objetos e LINQ to XML) o parâmetro é um 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 XML) the parameter is a delegate type System.Func<T,TResult>. Uma expressão lambda é a maneira mais conveniente de criar esse delegado.A lambda expression is the most convenient way to create that delegate. Quando você chama o mesmo método, por exemplo, na classe System.Linq.Queryable (como faz em LINQ to SQLLINQ to SQL), o tipo de parâmetro é uma System.Linq.Expressions.Expression<Func>, em que Func é qualquer delegado Func com até 16 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. Novamente, uma expressão lambda é apenas uma maneira muito concisa de construir essa árvore de expressão.Again, a lambda expression is just a very concise way to construct that expression tree. Os lambdas permitem que chamadas Where pareçam semelhantes embora, na verdade, o tipo de objeto criado do lambda seja diferente.The lambdas allow the Where calls to look similar although in fact the type of object created from the lambda is different.

No exemplo anterior, observe que a assinatura do delegado tem um parâmetro de entrada implícito inserido do tipo int e retorna um int.In the previous example, notice that the delegate signature has one implicitly-typed input parameter of type int, and returns an int. A expressão lambda pode ser convertida como um delegado desse tipo porque também tem um parâmetro de entrada (x) e um valor de retorno que o compilador pode converter implicitamente no 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. (A inferência de tipos é discutida em mais detalhes nas seções a seguir.) Quando o delegado for chamado usando um parâmetro de entrada 5, ele retornará 25 como resultado.(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.

Lambdas não são permitidas no lado esquerdo do operador is ou as.Lambdas are not allowed on the left side of the is or as operator.

Todas as restrições que se aplicam a métodos anônimos também se aplicam às expressões lambda.All restrictions that apply to anonymous methods also apply to lambda expressions. Para obter mais informações, consulte Métodos anônimos.For more information, see Anonymous Methods.

Lambdas de expressãoExpression Lambdas

Uma expressão lambda com uma expressão no lado direito do operador => é chamada de lambda de expressão.A lambda expression with an expression on the right side of the => operator is called an expression lambda. Os lambdas de expressão são usados amplamente na construção de Árvores de expressão.Expression lambdas are used extensively in the construction of Expression Trees. Uma expressão lambda retorna o resultado da expressão e tem o seguinte formato básico:An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

Os parênteses serão opcionais somente se o lambda tiver um parâmetro de entrada; caso contrário, eles serão obrigatórios.The parentheses are optional only if the lambda has one input parameter; otherwise they are required. Dois ou mais parâmetros de entrada são separados por vírgulas e envolvidos por parênteses:Two or more input parameters are separated by commas enclosed in parentheses:

(x, y) => x == y

Às vezes, é difícil ou impossível para o compilador inferir os tipos de entrada.Sometimes it is difficult or impossible for the compiler to infer the input types. Quando isso ocorre, você pode especificar os tipos de maneira explícita conforme mostrado neste exemplo:When this occurs, you can specify the types explicitly as shown in the following example:

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

Especifique parâmetros de entrada zero com parênteses vazios:Specify zero input parameters with empty parentheses:

() => SomeMethod()

Observe no exemplo anterior que o corpo de uma expressão lambda pode consistir de uma chamada de método.Note in the previous example that the body of an expression lambda can consist of a method call. No entanto, se você estiver criando árvores de expressão que serão avaliadas fora do .NET Framework, como no SQL Server, você não deverá usar chamadas de método em expressões 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. Os métodos não terão significado fora do contexto do .NET Common Language Runtime.The methods will have no meaning outside the context of the .NET common language runtime.

Lambdas de instruçãoStatement Lambdas

Um lambda de instrução lembra um lambda de expressão, exceto que as instruções estão incluídas entre chaves:A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(parâmetros de entrada) => { instrução; }(input-parameters) => { statement; }

O corpo de uma instrução lambda pode consistir de qualquer número de instruções; no entanto, na prática, normalmente não há mais de duas ou três.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); };

Lambdas de instrução, como métodos anônimos, não podem ser usados para criar árvores de expressão.Statement lambdas, like anonymous methods, cannot be used to create expression trees.

Lambdas assíncronosAsync Lambdas

Você pode facilmente criar expressões e instruções lambda que incorporem processamento assíncrono, ao usar as palavras-chaves async e await.You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. Por exemplo, o exemplo do Windows Forms a seguir contém um manipulador de eventos que chama e espera um método assíncrono 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);  
    }  
}  

Você pode adicionar o mesmo manipulador de eventos ao usar um lambda assíncrono.You can add the same event handler by using an async lambda. Para adicionar esse manipulador, adicione um modificador async antes da lista de parâmetros lambda, como mostra o exemplo a seguir.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 obter mais informações sobre como criar e usar os métodos assíncronos, consulte Programação assíncrona com async e await.For more information about how to create and use async methods, see Asynchronous Programming with async and await.

Lambdas com os operadores de consulta padrãoLambdas with the Standard Query Operators

Vários operadores de consulta padrão têm um parâmetro de entrada cujo tipo é de uma família 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. Esses delegados usam parâmetros de tipo para definir o número e os tipos de parâmetros de entrada e o tipo de retorno do delegado.These delegates use type parameters to define the number and types of input parameters, and the return type of the delegate. delegados Func são muito úteis para encapsular expressões definidas pelo usuário aplicadas a cada elemento em um conjunto de dados de origem.Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. Por exemplo, considere o seguinte tipo delegado:For example, consider the following delegate type:

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

O delegado pode ser instanciado como Func<int,bool> myFunc onde int é um parâmetro de entrada e bool é o valor de retorno.The delegate can be instantiated as Func<int,bool> myFunc where int is an input parameter and bool is the return value. O valor de retorno é sempre especificado no último parâmetro de tipo.The return value is always specified in the last type parameter. Func<int, string, bool> define um delegado com dois parâmetros de entrada, int e string e um tipo de retorno bool.Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. O delegado Func a seguir, quando é chamado, retornará true ou false para indicar se o parâmetro de entrada é 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  

Você também pode fornecer uma expressão lambda quando o tipo de argumento é Expression<Func>, por exemplo, nos operadores de consulta padrão que são definidos em 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. Quando você especifica um argumento Expression<Func>, o lambda será compilado em uma árvore de expressão.When you specify an Expression<Func> argument, the lambda will be compiled to an expression tree.

Um operador de consulta padrão, o método Count, é mostrado aqui: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);  

O compilador pode inferir o tipo de parâmetro de entrada ou você também pode especificá-lo explicitamente.The compiler can infer the type of the input parameter, or you can also specify it explicitly. Essa expressão lambda em particular conta esses inteiros (n) que, quando dividida por dois, tem um resto 1.This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

A linha de código a seguir gera uma sequência que contém todos os elementos na matriz numbers do lado esquerdo do 9 porque esse é o primeiro número na sequência que não atende à condição: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);  

Este exemplo mostra como especificar vários parâmetros de entrada ao inclui-los entre parênteses.This example shows how to specify multiple input parameters by enclosing them in parentheses. O método retorna todos os elementos na matriz de números até ser encontrado um número cujo valor seja menor do que sua posição.The method returns all the elements in the numbers array until a number is encountered whose value is less than its position. Não confunda o operador lambda (=>) com o operador greater than ou equal (>=).Do not confuse the lambda operator (=>) with the greater than or equal operator (>=).

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

Inferência de tipos em lambdasType Inference in Lambdas

Ao escrever lambdas, você geralmente não precisa especificar um tipo para os parâmetros de entrada porque o compilador pode inferir o tipo com base no corpo lambda, no tipo delegado do parâmetro e em outros fatores conforme descrito na especificação da linguagem 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 a maioria dos operadores de consulta padrão, a primeira entrada é o tipo dos elementos na sequência de origem.For most of the standard query operators, the first input is the type of the elements in the source sequence. Portanto, se você estiver consultando um IEnumerable<Customer>, a variável de entrada será inferida para ser um objeto Customer, o que significa que você tem acesso aos seus métodos e propriedades: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");  

As regras gerais para lambdas são:The general rules for lambdas are as follows:

  • O lambda deve conter o mesmo número de parâmetros do tipo delegado.The lambda must contain the same number of parameters as the delegate type.

  • Cada parâmetro de entrada no lambda deve ser implicitamente conversível em seu parâmetro delegado correspondente.Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • O valor de retorno do lambda (se houver) deve ser implicitamente conversível para o tipo de retorno do delegado.The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

Observe que as expressões lambda em si não têm um tipo porque o sistema de tipo comum não possui conceito intrínseco da "expressão lambda".Note that lambda expressions in themselves do not have a type because the common type system has no intrinsic concept of "lambda expression." No entanto, às vezes é conveniente falar informalmente do "tipo" de uma expressão lambda.However, it is sometimes convenient to speak informally of the "type" of a lambda expression. Nesses casos, o tipo se refere ao tipo delegado ou tipo Expression ao qual a expressão lambda é convertida.In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Escopo variável em expressões lambdaVariable Scope in Lambda Expressions

As lambdas podem se referir a variáveis externas (consulte Métodos Anônimos) que estão no escopo do método que define a função lambda ou no escopo do tipo que contém a expressão 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. As variáveis que são capturadas dessa forma são armazenadas para uso na expressão lambda mesmo que de alguma outra forma elas saíssem do escopo e fossem coletadas como lixo.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. Uma variável externa deve ser definitivamente atribuída para que possa ser consumida em uma expressão lambda.An outer variable must be definitely assigned before it can be consumed in a lambda expression. O exemplo a seguir demonstra estas regras: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();  
    }  
}  

As seguintes regras se aplicam ao escopo variável em expressões lambda:The following rules apply to variable scope in lambda expressions:

  • Uma variável capturada não será coletada do lixo até que o delegado que faz referência a ela se qualifique para coleta de lixo.A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • As variáveis introduzidas em uma expressão lambda não são visíveis no método externo.Variables introduced within a lambda expression are not visible in the outer method.

  • Uma expressão lambda não pode capturar diretamente um parâmetro ref ou out de um método delimitador.A lambda expression cannot directly capture a ref or out parameter from an enclosing method.

  • Uma instrução return em uma expressão lambda não faz com que o método delimitador retorne.A return statement in a lambda expression does not cause the enclosing method to return.

  • Uma expressão lambda não poderá conter uma instrução goto, break ou continue que está dentro da função lambda se o destino da instrução jump estiver fora do bloco.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. Também será um erro ter uma instrução jump fora do bloco da função lambda se o destino estiver dentro do bloco.It is also an error to have a jump statement outside the lambda function block if the target is inside the block.

Especificação da Linguagem C#C# Language Specification

Para obter mais informações, consulte a Especificação da linguagem C#. A especificação da linguagem é a fonte definitiva para a sintaxe e o uso de C#.

Expressões lambda, eventos e delegados em 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

Consulte tambémSee Also

Guia de Programação em C#C# Programming Guide
LINQ (Consulta Integrada à Linguagem)LINQ (Language-Integrated Query)
Métodos anônimosAnonymous Methods
isis
Árvores de ExpressãoExpression Trees
Visual Studio 2008 amostras c# (Consulte exemplos de consultas LINQ arquivos e programas do XQuery)Visual Studio 2008 C# Samples (see LINQ Sample Queries files and XQuery program)
Expressões lambda recursivasRecursive lambda expressions