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

Uma expressão lambda é um bloco de código (uma expressão ou um bloco de instruções) que é tratado como um objeto.A lambda expression is a block of code (an expression or a statement block) that is treated as an object. Ela pode ser passada como um argumento para métodos e também pode ser retornada por chamadas de método.It can be passed as an argument to methods, and it can also be returned by method calls. As expressões lambda são usadas amplamente para:Lambda expressions are used extensively for:

As expressões lambda são códigos que podem ser representados como um delegado ou como uma árvore de expressão que é compilada para um delegado.Lambda expressions are code that can be represented either as a delegate, or as an expression tree that compiles to a delegate. O tipo delegado específico de uma expressão lambda depende de seus parâmetros e do valor retornado.The specific delegate type of a lambda expression depends on its parameters and return value. As expressões lambda que não retornam um valor correspondem a um delegado Action específico, dependendo de seu número de parâmetros.Lambda expressions that don't return a value correspond to a specific Action delegate, depending on its number of parameters. As expressões lambda que retornam um valor correspondem a um delegado Func específico, dependendo de seu número de parâmetros.Lambda expressions that return a value correspond to a specific Func delegate, depending on its number of parameters. Por exemplo, uma expressão lambda que tem dois parâmetros, mas não retorna nenhum valor, corresponde a um delegado Action<T1,T2>.For example, a lambda expression that has two parameters but returns no value corresponds to an Action<T1,T2> delegate. Uma expressão lambda que tem um parâmetro e retorna um valor corresponde ao delegado Func<T,TResult>.A lambda expression that has one parameter and returns a value corresponds to Func<T,TResult> delegate.

Usa uma expressão lambda usa o =>, o operador de declaração lambda, para separar a lista de parâmetros de lambda de seu código executável.A lambda expression uses =>, the lambda declaration operator, to separate the lambda's parameter list from its executable code. Para criar uma expressão lambda, você especifica os parâmetros de entrada (se houver) no lado esquerdo do operador lambda e coloca 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 de linha única x => x * x especifica um parâmetro chamado x e retorna o valor de x ao quadrado.For example, the single-line 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:

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

Você também pode atribuir uma expressão lambda a um tipo de árvore de expressão:You also can assign a lambda expression to an expression tree type:

System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
Console.WriteLine(e);
// Output:
// x => (x * x)

Ou pode passá-la diretamente como um argumento de método:Or you can pass it directly as a method argument:

int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);
Console.WriteLine(string.Join(" ", squaredNumbers));
// Output:
// 4 9 16 25

Quando você usa a sintaxe baseada em método para chamar o método Enumerable.Select na classe System.Linq.Enumerable (assim como faz em LINQ to Objects e LINQ to XML), o parâmetro é um tipo delegado System.Func<T,TResult>.When you use method-based syntax to call the Enumerable.Select method in the System.Linq.Enumerable class (as you do in LINQ 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 método Queryable.Select na classe System.Linq.Queryable (como faz no LINQ to SQL), o tipo de parâmetro é um tipo de árvore de expressão Expression<Func<TSource,TResult>>.When you call the Queryable.Select method in the System.Linq.Queryable class (as you do in LINQ to SQL) the parameter type is an expression tree type Expression<Func<TSource,TResult>>. 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 Select pareçam semelhantes embora, na verdade, o tipo de objeto criado do lambda seja diferente.The lambdas allow the Select calls to look similar although in fact the type of object created from the lambda is different.

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.

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.

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

Action line = () => Console.WriteLine();

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:

Func<int, int, bool> testForEquality = (x, y) => x == y;

Às vezes, é difícil ou impossível para o compilador inferir os tipos de entrada.Sometimes it's impossible for the compiler to infer the input types. Você pode especificar os tipos de maneira explícita conforme mostrado neste exemplo:You can specify the types explicitly as shown in the following example:

Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;

Os tipos de parâmetro de entrada devem ser todos explícitos ou implícitos; caso contrário, ocorrerá o erro CS0748 de compilador.Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs.

O corpo de um lambda de expressão pode consistir em uma chamada de método.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 contexto do .NET Common Language Runtime, como no SQL Server, você não deverá usar chamadas de método em lambdas de expressão.However, if you are creating expression trees that are evaluated outside the context of the .NET common language runtime, 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:

(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.

Action<string> greet = name => 
{ 
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
};
greet("World");
// Output:
// Hello World!

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();
        button1.Click += button1_Click;
    }

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

    private 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) =>
        {
            await ExampleMethodAsync();
            textBox1.Text += "\r\nControl returned to Click event handler.\n";
        };
    }

    private 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.

Expressões lambda e tuplasLambda expressions and tuples

A partir do C# 7.0, a linguagem C# fornece suporte interno para tuplas.Starting with C# 7.0, the C# language provides built-in support for tuples. Você pode fornecer uma tupla como um argumento para uma expressão lambda e a expressão lambda também pode retornar uma tupla.You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. Em alguns casos, o compilador do C# usa a inferência de tipos para determinar os tipos dos componentes da tupla.In some cases, the C# compiler uses type inference to determine the types of tuple components.

Você pode definir uma tupla, colocando entre parênteses uma lista delimitada por vírgulas de seus componentes.You define a tuple by enclosing a comma-delimited list of its components in parentheses. O exemplo a seguir usa a tupla com três componentes para passar uma sequência de números para uma expressão lambda, que dobra cada valor e retorna uma tupla com três componentes que contém o resultado das multiplicações.The following example uses tuple with three components to pass a sequence of numbers to a lambda expression, which doubles each value and returns a tuple with three components that contains the result of the multiplications.

Func<(int, int, int), (int, int, int)> doubleThem = ns => (2 * ns.Item1, 2 * ns.Item2, 2 * ns.Item3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");
// Output:
// The set (2, 3, 4) doubled: (4, 6, 8)

Normalmente, os campos de uma tupla são chamados de Item1, Item2, etc. No entanto, você pode definir uma tupla com componentes nomeados, como é feito no exemplo a seguir.Ordinarily, the fields of a tuple are named Item1, Item2, etc. You can, however, define a tuple with named components, as the following example does.

Func<(int n1, int n2, int n3), (int, int, int)> doubleThem = ns => (2 * ns.n1, 2 * ns.n2, 2 * ns.n3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");

Para saber mais sobre tuplas C#, confira o artigo sobre tipos de tuplas C#.For more information about C# tuples, see C# tuple types.

Lambdas com os operadores de consulta padrãoLambdas with the standard query operators

O LINQ to Objects, entre outras implementações, tem um parâmetro de entrada cujo tipo faz parte da família de delegados genéricos Func<TResult>.LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. Esses delegados usam parâmetros de tipo para definir o número e o tipo de parâmetros de entrada e o tipo de retorno do delegado.These delegates use type parameters to define the number and type 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 Func<T,TResult>:For example, consider the Func<T,TResult> delegate type:

public delegate TResult Func<in T, out TResult>(T arg)

O delegado pode ser instanciado como um Func<int, bool>, em que int é um parâmetro de entrada e bool é o valor de retorno.The delegate can be instantiated as a Func<int, bool> instance 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. Por exemplo, Func<int, string, bool> define um delegado com dois parâmetros de entrada, int e string, e um tipo de retorno de bool.For example, 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 é invocado, retornará um valor booliano que indica se o parâmetro de entrada é ou não igual a cinco:The following Func delegate, when it's invoked, returns Boolean value that indicates whether the input parameter is equal to five:

Func<int, bool> equalsFive = x => x == 5;
bool result = equalsFive(4);
Console.WriteLine(result);   // False

Você também pode fornecer uma expressão lambda quando o tipo de argumento é um Expression<TDelegate>. Por exemplo, nos operadores de consulta padrão que são definidos no tipo Queryable.You can also supply a lambda expression when the argument type is an Expression<TDelegate>, for example in the standard query operators that are defined in the Queryable type. Quando você especifica um argumento Expression<TDelegate>, o lambda é compilado em uma árvore de expressão.When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree.

O exemplo a seguir usa o operador padrão de consulta Count:The following example uses the Count standard query operator:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.WriteLine($"There are {oddNumbers} odd numbers in {string.Join(" ", numbers)}");

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.

O exemplo a seguir gera uma sequência que contém todos os elementos da matriz numbers que precedem o 9, porque esse é o primeiro número na sequência que não satisfaz a condição:The following example produces a sequence that contains all elements in the numbers array that precede the 9, because that's the first number in the sequence that doesn't meet the condition:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstNumbersLessThanSix = numbers.TakeWhile(n => n < 6);
Console.WriteLine(string.Join(" ", firstNumbersLessThanSix));
// Output:
// 5 4 1 3

O exemplo a seguir especifica vários parâmetros de entrada, colocando-os entre parênteses.The following example specifies multiple input parameters by enclosing them in parentheses. O método retorna todos os elementos na matriz numbers até encontrar um número cujo valor seja inferior à sua posição ordinal na matriz:The method returns all the elements in the numbers array until it encounters a number whose value is less than its ordinal position in the array:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
Console.WriteLine(string.Join(" ", firstSmallNumbers));
// Output:
// 5 4

Inferência de tipos em expressões lambdaType inference in lambda expressions

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 do lambda, nos tipos de parâmetro e em outros fatores, conforme descrito na especificação da linguagem C#.When writing lambdas, you often don't have to specify a type for the input parameters because the compiler can infer the type based on the lambda body, the parameter types, 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. Se você estiver consultando um IEnumerable<Customer>, a variável de entrada será inferida para ser um objeto Customer, o que significa que você terá acesso aos seus métodos e propriedades: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 a inferência de tipos para lambdas são as seguintes:The general rules for type inference 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 nenhum conceito intrínseco de "expressão lambda."Note that lambda expressions in themselves don't 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's 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

Os lambdas podem se referir a variáveis externas (confira os Métodos Anônimos) que estão no escopo do método que define a expressã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 expression, 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:

public static class VariableScopeWithLambdas
{
    public class VariableCaptureGame
    {
        internal Action<int> updateCapturedLocalVariable;
        internal Func<int, bool> isEqualToCapturedLocalVariable;

        public void Run(int input)
        {
            int j = 0;

            updateCapturedLocalVariable = x =>
            {
                j = x;
                bool result = j > input;
                Console.WriteLine($"{j} is greater than {input}: {result}");
            };

            isEqualToCapturedLocalVariable = x => x == j;

            Console.WriteLine($"Local variable before lambda invocation: {j}");
            updateCapturedLocalVariable(10);
            Console.WriteLine($"Local variable after lambda invocation: {j}");
        }
    }

    public static void Main()
    {  
        var game = new VariableCaptureGame();
        
        int gameInput = 5;
        game.Run(gameInput);

        int jTry = 10;
        bool result = game.isEqualToCapturedLocalVariable(jTry);
        Console.WriteLine($"Captured local variable is equal to {jTry}: {result}");

        int anotherJ = 3;
        game.updateCapturedLocalVariable(anotherJ);

        bool equalToAnother = game.isEqualToCapturedLocalVariable(anotherJ);
        Console.WriteLine($"Another lambda observes a new value of captured variable: {equalToAnother}");
    }
    // Output:
    // Local variable before lambda invocation: 0
    // 10 is greater than 5: True
    // Local variable after lambda invocation: 10
    // Captured local variable is equal to 10: True
    // 3 is greater than 5: False
    // Another lambda observes a new value of captured variable: True
}

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 delimitador.Variables introduced within a lambda expression are not visible in the enclosing method.

  • Uma expressão lambda não pode capturar um parâmetro in, ref ou out diretamente de um método delimitador.A lambda expression cannot directly capture an in, ref, or out parameter from the 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 doesn't cause the enclosing method to return.

  • Uma expressão lambda não pode conter uma instrução goto, break ou continue se o destino daquela instrução de salto estiver fora do bloco da expressão lambda.A lambda expression cannot contain a goto, break, or continue statement if the target of that jump statement is outside the lambda expression block. Também será um erro ter uma instrução de salto fora do bloco da expressão lambda se o destino estiver dentro do bloco.It's also an error to have a jump statement outside the lambda expression block if the target is inside the block.

Especificação da linguagem C#C# language specification

Para obter mais informações, confira a seção Expressões de função anônima da Especificação da linguagem C#.For more information, see the Anonymous function expressions section of the C# language specification.

Delegados, eventos e expressões lambda no Manual de instruções C# 3.0, terceira edição: mais de 250 soluções para programadores do C# 3.0Delegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Consulte tambémSee also