O que são Lambda Expressions?    

Lambda Expression para muitos pode ser um assunto simples, uma pratica simples, porém, sempre que começo a usá-las em meu código, vejo muitas interrogações nas pessoas em minha volta, e participando do TechEd neste ano, pude perceber que isso é muito comum, porque quando estava saindo de uma palestra do Otavio Pecego Coelho, sobre paralelismo, ouvi muitas pessoas comentando: “Putz, muito louco, mas quando começa a entrar um monte de ‘Lambda’ começa a ficar meio complicado.” .Bom, não sei como isso pode ser possível, visto que Lambda Expression só veio para facilitar, como veremos ao longo desse post.

Pensando nisso, e conversando com um amigo meu, ele me deu a sugestão de escrever sobre isso aqui, e lá vai. Caso tenham dúvidas ou sugestões, please let me know!

Um pouco do histórico

As expressões Lambdas não são uma grande inovação do time do .Net, na realidade, como a maioria das novidades que surgiram entre o .Net 3.0 e 3.5, elas são evoluções naturais dos rumos que a C# vinha tomando desde a sua criação. Vamos ver como isso aconteceu.

Delegates

Delegate é o princípio de tudo. Delegate resumidamente é um tipo por referência, que armazena ponteiros para métodos. Com a criação de uma Delegate, você defini a assinatura de um método que pode ser armazenado em uma variável do tipo dessa Delegate, ou seja, os métodos “apontados” nas variáveis de tipo Delegate devem seguir as regras definidas na Delegate.

Podemos usar Delegates para “delegar” a responsabilidade de execução de uma lógica específica a um outro método, que pode ser implementado depois.

A definição de uma delegate é a seguinte:

[modificador] Delegate [tipo de retorno [Nome da Delegate] ([parâmetros de entrada])

public delegate int Conta(int v1,int v2);

Definindo essa Delegate, qualquer método que tenha essa assinatura pode ser delegado a uma variável do tipo Conta. Os eventos basicamente utilizam Delegates para definir qual deve ser a assinatura de um método que implementa um evento. A Delegate do evento basicamente é a seguinte:

public delegate void Evento(object sender, EventArgs args)

E como podemos utilizar? O exemplo padrão sempre é o da calculadora. Imagina que queiramos delegar ao usuário a opção de escolher qual operação ele deseja executar. Vamos criar dois método, Somar e Subtrair. O código completo de uma aplicação console está abaixo:

    delegate int Conta(int v1,int v2);
    class Program
    {
        static int Soma(int valor1, int valor2)
        {
            return valor1 + valor2;
        }
        static int Subtrair(int valor1, int valor2)
        {
            return valor1 + valor2;
        }

        static void Main(string[] args)
        {
            Console.Write("Valor 1:");
            var v1 = Console.ReadLine();
            Console.Write("Valor 2:");
            var v2 = Console.ReadLine();
            Console.Write("Operação(+ ou -):");
            var op = Console.ReadLine();

            //Operação padrão é soma
            Conta operacao = Soma;

            switch (op)
            {
                case "+":
                    operacao = Soma;
                    break;
                case "-":
                    operacao = Subtrair;
                    break;
            }

            //Chamo o método que o usuário selecionou, sem me importar qual é
            var ret = operacao(Convert.ToInt32(v1), Convert.ToInt32(v2));

            Console.WriteLine("Resultado: {0}", ret);
            Console.ReadKey();
        }
    }

Assim estamos delegando ao usuário qual método utilizar. Com esse entendimento, podemos já perceber, como a equipe do C# percebeu, vários momentos onde utilizar ponteiros de métodos por parâmetro.

Obs.: Como Delegate obriga a implementação de uma assinatura, por isso os tipos e a quantidade de parâmetros são importantes, porém não o nome dos parâmetros.

Em Collections podemos ver o uso desta técnica em muitos métodos, por exemplo, o Sort, Find, etc. A equipe do .Net teve o segundo dialogo (obviamente o dialogo seguinte é inventado):

“Por que não podemos ter um Collection qualquer, e deixar que o programador se preocupe em como ordenar? “

“Sim, e para isso ele passa por parâmetro o método, desde que ele implemente a assinatura, respeitando uma Delegate”

Certo, então da mesma forma que podemos criar variáveis de tipos de Delegate, podemos utilizar parâmetros de tipos Delegate. Por exemplo, para utilizar o método Find, implementamos uma Delegate chamada Predicate, inclusive a utilização dos métodos de Collection são de predicados por esse motivo.

public delegate bool Predicate(T obj);

Onde T é o tipo da sua lista, ou seja, uma lista List<int> o T seria int. A implementação do método abaixo seria a seguinte:

	static bool Buscar(int valor)
        {
            return valor == 1;
        }

Sua chamada seria a seguinte:

           List<int> lista = new List<int>();
            //alimenta a lista
            
            var itemEncontrado = lista.Find(Buscar);

Certo, com isso entendemos como funciona uma Delegate, então vamos evoluir até chegar a Lambda Expression.

Métodos Anônimos

A equipe do C# percebeu que a adoção de Delegates era muito boa, porém, é um tanto trabalhosa, pois você precisa criar métodos sempre que precisar utilizar Delegate, o que te faz ter que digitar mais, e o seu código pode ficar mais confuso.

Imagine que você precisa realizar a busca citada acima apenas em um trecho especifico de código, você teria a necessidade de criar um método, mesmo que sua utilização fosse extremamente restrita, e o pessoal da Microsoft pensou: “Por que não criarmos apenas o código do método, e não o método em si? Ficaria mais simples e mais rápido”.

Ótimo idéia, e assim entra o método anônimo no jogo.

Métodos anônimos são métodos, como o próprio nome diz, sem nomes. São métodos criados dentro de outros métodos, adicionando seu conteúdo diretamente em uma variável ou parâmetro do tipo Delegate.

Por exemplo, em vez de criarmos o método Busca acima, poderíamos colocar o seu código diretamente na chamada do método:

	var item = lista.Find(delegate (int valor){
                return valor == 1;
            });

O resultado seria o mesmo, com a diferença de não precisarmos criar um novo método na classe. Essa técnica pode ser utilizada em qualquer situação onde usaríamos o método ponteiro para a Delegate, ou seja, variáveis, parâmetros, eventos, etc.

Finalmente Lambda Expression

Bom, diante de tudo que vimos acima, percebemos que codificar “on the fly” dessa forma agiliza muito o processo de desenvolvimento, além de não deixar códigos muito específicos por toda parte da classe.

Mas ai ainda percebemos que digitamos muito além do necessário. Pensa comigo:

“Para que a palavra chave “delegate”? Afinal, eu já sei que é uma delegate.”

“Para que ter que definir o tipo do parâmetro?” Afinal, se o compilador já sabe qual é a Delegate a ser utilizada, logo ele já sabe quais são os os tipos e a quantidade dos parâmetros.”

“E as vezes o método é tão simples, que a única linha já é o retorno, então para que utilizar o return?”

Aí mais uma vez o time do C# deu uma forcinha, dizendo: “Vamos simplificar tudo criando um novo operador que serve para separar os parâmetros do método do corpo do método e pronto!”

E o Find da Collection ficou assim:

var item = lista.Find(valor=>valor == 1);

Agora sim o método ficou sucinto, o que está antes do => (igual maior) são os parâmetros de entrada, e o que está depois é o retorno do método caso tenha só uma linha, ou o corpo do método, caso tenha mais de uma linha.

Se quisermos criar uma Lambda Expression para nossa delegate Conta criada acima, seria o seguinte:

 
operacao  = (valor1, valor2)=> valor1 + valor2;
var ret = operacao(Convert.ToInt32(v1), Convert.ToInt32(v2));

Muito simples! Perceba que para mais de um parâmetro, adicionamos parênteses. Caso o corpo do método seja mais complexo, com mais de uma linha, teremos que utilizar chaves e no final utilizar a palavra chave return.

Conclusão

Neste post vimos como Lambda Expression nada mais é que uma evolução natural das técnicas utilizadas pelo C# desde sua primeira versão.

Por ser uma evolução, isso só pode surgir para ajudar e simplificar nossa vida. Caso não esteja sendo claro em nossas cabeças as evoluções que a linguagem vem tomando, é importante que entendamos o porquê das mudanças.

Muitos já criticaram o “dynamic” do C#, sem ainda entender o porque de se utilizá-lo. Vou tentar abordar isso em um momento oportuno.

Bom, espero que tenham gostado desse post.

Até o próximo!

24. September 2010 06:51 by Frederico | Comments (65) | Permalink

Sobre mim

Minha Imagem

Meu nome é Frederico Batista Emídio, trabalho com desenvolvimento de sistemas profissionalmente a oito anos, porém, já faço sites pessoais a pelo menos dez anos.

Para saber mais clique aqui.

Páginas

Calendário

<<  November 2014  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET