Expressões lambda (guia de programação de C#)

A expressão lambda é uma função anônima que pode conter expressões e instruções e pode ser usada para criar tipos de árvore de delegados ou expressão.

Todas as expressões lambda usam o operador lambda = >, que é lido como "vai". O lado esquerdo do operador lambda especifica os parâmetros de entrada (se houver) e o direito contém a expressão ou o bloco de instruções A expressão lambda x => x * x é lido "x vai para tempos de x." Esta expressão pode ser atribuída a um tipo de delegado da seguinte maneira:

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:

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 como atribuição (=) e é associativos à direita.

Lambdas são usados em baseado no método LINQ consultas como argumentos para métodos de operador de consulta padrão como Where.

Quando você usa a sintaxe do método para chamar o Where método na Enumerable classe (como você faz no LINQ a objetos e LINQ to XML) o parâmetro é um tipo delegado System.Func<T, TResult>. Uma expressão lambda é a maneira mais conveniente para criar esse representante. Quando você chamar o mesmo método, por exemplo, o System.Linq.Queryable classe (como em LINQ to SQL) o tipo de parâmetro é um System.Linq.Expressions.Expression<Func> onde Func é qualquer representantes Func com até dezesseis parâmetros de entrada. Novamente, uma expressão lambda é apenas uma maneira bastante concisa construir a árvore de expressão. Os lambdas permitem que o Where chamadas de aparência semelhante, embora na verdade, o tipo de objeto criado a partir do lambda é diferente.

No exemplo anterior, observe que a assinatura do delegado tenha um tipo implícito do parâmetro do tipo de entrada inte retorna um int. A expressão lambda pode ser convertida para um representante desse tipo porque ele também tem um parâmetro de entrada (x) e um valor de retorno, o compilador pode converter implicitamente digitar int. (Inferência de tipo é discutida mais detalhadamente nas seções a seguir). Quando o delegado é chamado usando um parâmetro de entrada de 5, ele retorna um resultado de 25.

Lambdas não são permitidos no lado esquerdo do é ou como operador.

Todas as restrições que se aplicam aos métodos anônimos também se aplicam as expressões lambda. Para obter mais informações, consulte Anonymous Methods (C# Programming Guide).

Expressão Lambdas

Uma expressão lambda com uma expressão no lado direito é chamada um de expressão lambda. Lambdas de expressão são amplamente usados na construção de Árvores de expressão (C# e Visual Basic). Uma expressão lambda retorna o resultado da expressão e assume a forma básica a seguir:

(input parameters) => expression

Os parênteses são opcionais apenas se o lambda tem um parâmetro de entrada; Caso contrário, eles são necessários. Dois ou mais parâmetros de entrada são separados por vírgulas entre parênteses:

(x, y) => x == y

Às vezes é difícil ou impossível para o compilador para inferir os tipos de entrada. Quando isso ocorrer, você pode especificar os tipos explicitamente, conforme mostrado no exemplo a seguir:

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

Especifica zero parâmetros de entrada com parênteses vazios:

() => SomeMethod()

Observe no exemplo anterior que o corpo de uma expressão lambda pode consistir de uma chamada de método. No entanto, se você estiver criando árvores de expressão que serão consumidos em outro domínio, como, por exemplo, SQL Server, você não deve usar chamadas de método em expressões lambda. Os métodos não terão nenhum significado fora do contexto da linguagem comum de tempo de execução do .NET.

Instrução Lambdas

Uma instrução lambda é semelhante a uma expressão lambda exceto pelo fato de a instrução(ões) é colocada entre chaves:

(input parameters) => {statement;}

O corpo do lambda instrução pode consistir em qualquer número de instruções; No entanto, na prática há normalmente, não há mais de dois ou três.

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

Lambdas de instrução, como métodos anônimos, não podem ser usados para criar árvores de expressão.

Lambdas com operadores de consulta padrão

Muitos operadores de consulta padrão tem um parâmetro de entrada cujo tipo é um do Func<T, TResult> família de representantes genéricos. O Func<T, TResult> delegados usam parâmetros de tipo para definir o número e tipo de parâmetros de entrada e o tipo de retorno do delegado. Funcdelegados são muito úteis para encapsular expressões definidas pelo usuário que são aplicadas a cada elemento em um conjunto de dados de origem. Por exemplo, considere o seguinte tipo de delegado:

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. O valor de retorno é sempre especificado no último parâmetro de tipo. **Func<int, string, bool>**define um delegado com dois parâmetros de entrada, int e stringe um tipo de retorno de bool. O seguinte Func delegado, quando ele é chamado, retornará true ou false para indicar se o parâmetro de entrada é igual a 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 é um Expression<Func>, por exemplo, em operadores de consulta padrão definidos no System.Linq.Queryable. Quando você especificar um Expression<Func> argumento, será compilado lambda a uma árvore de expressão.

Um operador de consulta padrão, o Count método, é mostrado aqui:

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ê pode também especificá-lo explicitamente. Essa expressão lambda determinada conta esses números inteiros (n) que quando divididos por dois têm uma restante 1.

O método a seguir produzirá uma seqüência que contém todos os elementos de numbers matriz estão à esquerda de 9, porque esse é o primeiro número na seqüência que não atenda a condição:

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

Este exemplo mostra como especificar vários parâmetros de entrada, colocando-os entre parênteses. O método retorna todos os elementos da matriz de números, até que um número é encontrado, cujo valor é menor do que a sua posição. Não confunda o operador lambda (=>) com o operador maior ou igual (>=).

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

Inferência Lambdas

Ao escrever lambdas, você normalmente não precisará especificar um tipo para os parâmetros de entrada, porque o compilador pode inferir o tipo com base no corpo do lambda, o tipo delegado subjacente e outros fatores, como descrito na especificação de linguagem C#. Para a maioria dos operadores de consulta padrão, a primeira entrada é o tipo dos elementos na seqüência de origem. Portanto, se você estiver consultando um IEnumerable<Customer>, e em seguida, a variável de entrada é inferida para ser um Customer objeto, o que significa que você tem acesso a seus métodos e propriedades:

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

As regras gerais para lambdas são:

  • Lambda deve conter o mesmo número de parâmetros, como o tipo de delegado.

  • Cada parâmetro de entrada na lambda deve ser implicitamente conversível para o parâmetro do delegate correspondente.

  • O valor de retorno do lambda (se houver) deve ser implicitamente conversível no tipo de retorno do delegado.

Observe que as expressões lambda em si não tem um tipo porque o common type system tem nenhum intrínseco conceito de "expressão lambda." No entanto, às vezes é conveniente fala informalmente de "tipo" de uma expressão lambda. Nesses casos o tipo refere-se no tipo delegate ou Expression Digite a expressão lambda é convertida.

Escopo de variáveis em expressões Lambda

Lambdas podem referir-se variáveis externas que estão no escopo no método delimitador ou tipo no qual o lambda é definido. Variáveis que são capturadas dessa maneira são armazenadas para uso na expressão lambda, mesmo se as variáveis de alguma forma passam fora do escopo e ser coletados pelo lixo. Uma variável externa deve ser atribuída definitivamente antes que ele pode ser consumido em uma expressão lambda. O exemplo a seguir demonstra estas regras:

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 aplicam-se o escopo de variáveis em expressões lambda:

  • Uma variável que é capturada não serão lixo-coletada até que o delegado que faz referência a ele sai do escopo.

  • Variáveis introduzidas em uma expressão lambda não são visíveis no método externo.

  • Uma expressão lambda diretamente não é possível capturar uma ref ou out parâmetro do método delimitador.

  • Uma instrução return em uma expressão lambda não faz com que o método delimitador para retornar.

  • Uma expressão lambda não pode conter um goto instrução, break de instrução, ou continue instrução cujo destino está fora do corpo ou no corpo de uma função anônima contido.

Especificação da linguagem C#

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

Capítulo do livro em destaque

Delegates, Events, and Lambda ExpressionsemC# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Consulte também

Referência

Anonymous Methods (C# Programming Guide)

é (referência de C#)

Conceitos

C# Programming Guide

Árvores de expressão (C# e Visual Basic)

Outros recursos

LINQ (consulta integrada à linguagem)

As expressões lambda recursiva