Desenvolvimento

17 ago, 2018

C# – Expressões Lambda (revisitado)

Publicidade

Neste artigo vamos rever os conceitos básicos das Expressões Lambda na linguagem C#. Começaremos revendo a definição formal dessas expressões.

O que é uma expressão lambda?

Uma expressão Lambda é uma função anônima que você pode usar para criar delegados ou tipos de árvore de expressão. 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. Essas expressões são particularmente úteis para escrever expressões de consulta LINQ.

Como criar um expressão lambda?

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.

Por exemplo, a expressão lambda x => x * x especifica um parâmetro chamado “x” e retorna o valor de x ao quadrado.

Você pode atribuir essa expressão a um tipo delegate da seguinte forma:

delegate int delegado(int i);  

static void Main(string[] args)  
{  
    delegado meuDelegate = x => x * x;  
    int valor = meuDelegate(5);  
}

Onde e por que usamos expressões lambda?

As expressões lambda são usadas em consultas LINQ baseadas em métodos como argumentos para métodos de operador de consulta padrão, como Where.

Dessa forma, as expressões lambda reduzem a quantidade de trabalho necessária ao iterar com dados. Todos os dados enumeráveis são consultáveis por meio do LINQ com o uso de uma consulta semelhante às consultas SQL.

Observação: A LINQ possui um mecanismo embutido para fazer um loop através de dados enumeráveis e usar certas condições de configuração nas cláusulas where para filtrar os dados.

As expressões lambda funcionam de maneira semelhante. Alguns dos métodos de extensão existentes apresentam expressões lambda incorporadas para executar determinadas ações e facilitar a execução de trabalhos repetitivos. (Vamos ver isso mais a frente)

Nota: As expressões lambda não exigem métodos de extensão para funcionar, mas esse é o uso mais comum atualmente.

Expressões Lambda no.NET Framework

Existe uma variedade de métodos de extensão definidos no .NET Framework que usam Expressões Lambda. No assembly System.Core, o namespace System.Linq apresenta duas classes estáticas contendo uma variedade de extensões para fontes IEnumerable e IQueryable.

Algumas dessas expressões são definidas no método Where, por exemplo, e o método Where é definido como:

Where<TSource> (IEnumerable <TSource>, Func<TSource, bool>);

Aqui, temos: que Func<> define uma expressão lambda, onde os parâmetros genéricos são a chave. O primeiro parâmetro (TSource) é o tipo que está sendo avaliado na expressão lambda, e o segundo parâmetro (bool) é o valor que precisa ser retornado pelo método.

Mas como usamos as expressões do Lambda?

Em C#, a expressão lambda é definida assim:

var clientes = lista.Where (cli => cli.Nome == "Macoratti");

Neste código, o operador “=>” denota uma expressão lambda. O parâmetro “cli” é o alias ou nome para o objeto do tipo <TSource> como mostrado acima na instrução Func, e o primeiro parâmetro é o objeto sendo iterado, ou o tipo de item dos itens na coleção.

Por exemplo, se você tem uma Lista<MeuObject>,cli” é uma referência para o tipo MeuObject. Portanto, em nossa expressão acima, o resultado (que deve ser um booleano) só recebe objetos onde o nome é “Macoratti“.

Tudo à direita do operador “=>” está definindo os critérios para verificar se o método retorna verdadeiro e falso.

Como é usado?

A seguir temos um exemplo onde definimos um delegate Func<string,int> com expressão lambda e com delegate:

using System;
using static System.Console;
namespace ExpressaoLambda1
{
    class Program
    {
        //Expressão Lambda
        static Func<string, int> tamanhoTextExpressaoLambda = (text => text.Length);
        //Expressão Delegate
        static Func<string, int> tamanhoTextoDelegate = delegate (string text) { return text.Length; };
        static void Main(string[] args)
        {
            WriteLine("Usando Expressão Lambda");
            WriteLine(tamanhoTextExpressaoLambda("Macoratti .net quase tudo para .NET."));
            WriteLine("Usando Delegate");
            WriteLine(tamanhoTextoDelegate("Macoratti .net quase tudo para .NET."));
            ReadLine();
        }
    }
}

Ambas as declarações são funções anônimas onde temos que a expressão lambda é a sintaxe mais recente e a mais concisa. Ambas são formas distintas de obter o mesmo resultado. Muitos operadores de consulta padrão têm um parâmetro de entrada cujo tipo é de delegate genérico Func<T, TResult>. Esses representantes 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.

Os delegados de Func 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.

O delegado pode ser instanciado como Func<int, bool> meuFunc, em que int é um parâmetro de entrada e bool é o valor de retorno. O valor de retorno é sempre especificado no último parâmetro de tipo.

Assim, Func<int, string, bool> define um delegado com dois parâmetros de entrada: int e string, e um tipo de retorno de bool. No código abaixo o delegado Func, quando invocado, retornará verdadeiro ou falso para indicar se o parâmetro de entrada é igual a 5:

Func<int, bool> meuFunc = x => x == 5;
bool resultado = meuFunc(4); // retorna false

Observe que expressões lambda (=> ) também podem ser usadas para formar Lambda Expression, onde você cria uma Árvore de Expressões (Expression Trees) em vez de um delegado.

Isso é bom, já que você pode usar a mesma sintaxe se estiver usando o LINQ to Objects (que é baseado em delegados como Func <T, TResult>) ou LINQ to Entities (que usa IQueryable <T> e expression trees).

Estamos conversados!