Back-End

13 fev, 2015

C# – Usando expressões lambdas para implementar delegates – Parte 02

Publicidade

Neste artigo eu vou continuar o artigo anterior e mostrar como podemos usar expressões lambdas para implementar delegates na linguagem C#. Vamos implementar o cálculo da raiz quadrada de segundo grau usando métodos anônimos e mostrar como simplificar o código usando expressões lambdas e delegates como Func<T>.

Para você poder acompanhar e entender este artigo você tem que saber o que é um delegate. Se você tem dúvidas leia o meu artigo: C# – Delegates e Eventos: Conceitos básicos e depois prossiga na leitura.

Eu não vou entrar em detalhes sobre o que é um delegate (para detalhes leia o artigo) vou apenas apresentar a sua definição (uma delas):

“Um Delegate é um ponteiro para um método. Um Delegate pode ser passado como um parâmetro para um método. Podemos mudar a implementação do método dinamicamente em tempo de execução, a única coisa que precisamos seguir fazendo isso seria manter o tipo de parâmetro e o tipo de retorno.”

Recursos usados:

Definindo delegates com expressões lambdas

As expressões lambdas são funções que podem conter expressões e declarações que são usadas para criar delegates e árvores de expressões, onde o tipo das variáveis não precisam ser declarados, visto que elas usam métodos anônimos.

Uma expressão lambda é uma função anônima que você pode usar para criar delegados ou tipos de árvore de expressão. Usando expressões lambda, você pode gravar funções locais, que podem ser passadas como argumentos ou serem retornadas como o valor de chamadas de função. Expressões lambda são particularmente úteis para escrever expressões de consulta LINQ. http://msdn.microsoft.com/pt-br/library/vstudio/bb397687.aspx

Como uma expressão lambda é uma expressão ela somente pode ser usada como parte de uma instrução. Desta forma podemos declarar a expressão lambda e chamar a função passando um valor ao parâmetro:

C#: y => y * y

VB .NET: Function(y) y * y

Na linguagem C#, todas as expressões usam o operador lambada => que significa “vá para”.

O lado esquerdo do operador lambda especifica os parâmetros de entrada (se houver) e o lado direito trata a expressão ou bloco de instruções.

Uma expressão lambda é uma forma reduzida de escrever métodos anônimos. Veja o exemplo abaixo onde temos um delegate reescrito usando uma expressão lambda:

  delegate(int x)
{
return(x+1);
}
 x=> x + 1
  delegate  expressão lambda

A sintaxe de uma expressão lambda lembra a de uma função padrão. As diferenças são as seguintes:

  • Uma expressão lambda não tem um nome;
  • Expressões lambda não podem ter modificadores, como Overloads ou Overrides;
  • Expressões lambda não usam uma cláusula ‘As’ para designar o tipo de retorno da função. Em vez disso, o tipo é inferido do valor que o corpo da expressão lambda avalia. Por exemplo, se o corpo da expressão lamba for Where cli.City = “Brazil”, seu tipo de retorno é Boolean;
  • O corpo da função deve ser uma expressão, não uma instrução. O corpo pode consistir de uma chamada para um procedimento de função, mas não uma chamada para um procedimento sub;
  • Nas expressões lambada não existe uma instrução Return. O valor retornado pela função é o valor da expressão no corpo da função;
  • Ou todos os parâmetros devem ter tipos de dados especificados ou todos devem ser inferidos;
  • Parâmetros opcionais e ParamArray não são permitidos;
  • Parâmetros genéricos não são permitidos.

Nota: Com a evolução da plataforma .NET e os novos recursos que foram introduzidos, a sintaxe das expressões lambda foi simplificada, e desta forma você poderá encontrar a mesma função escrita de maneiras diferentes, mas o resultado final será o mesmo.

Vamos voltar ao nosso exemplo de implementação para resolver uma equação do segundo grau. Na primeira parte do artigo fizemos a implementação usando delegates com métodos anônimos.

Como ficaria a nossa implementação usando expressões lambdas? O delegate EquacaoSegundoGrauDelegate continuaria da mesma forma:

delegate double EquacaoSegundoGrauDelegate(double a, double b, double c);

Mas na implementação do cálculo do discriminante e das raízes vamos usar expressões lambda.

Lembre-se que temos que passar 3 parâmetros (a, b, c) e, pela definição de expressão lambda, temos que colocar esta expressão no lado esquerdo do operador (=>).

No lado direito teremos que definir nossa implementação da fórmula usando esses parâmetros. Veja como ficou:

 


EquacaoSegundoGrauDelegate discriminante = (a,b,c) =>
(b * b – 4d * a * c);
 Implementação do cálculo discriminante

EquacaoSegundoGrauDelegate raiz1 = (a,b,c) =>
(-b + Math.Sqrt(discriminante(a, b, c))) / (2 * a);
 Cálculo da primeira raiz

EquacaoSegundoGrauDelegate raiz2 = (a,b,c) =>
(-b – Math.Sqrt(discriminante(a, b, c))) / (2 * a);
  Cálculo da segunda raiz

As expressões lambdas estão definidas e destacadas na cor vermelha.

Dessa forma, abaixo temos as implementações dos delegates usando expressões lambdas com três exemplos de cálculos feitos:

static void Metodo2()
        {
            Console.WriteLine("Método #2");
            // cria um método anônimo para calcular o discriminante
            EquacaoSegundoGrauDelegate discriminante = (a,b,c) => (b * b - 4d * a * c); 
            // cria métodos anônimos para calcular as raízes da equação
            EquacaoSegundoGrauDelegate raiz1 = (a,b,c) => (-b + Math.Sqrt(discriminante(a, b, c))) / (2 * a); 
            EquacaoSegundoGrauDelegate raiz2 = (a,b,c) => (-b - Math.Sqrt(discriminante(a, b, c))) / (2 * a); 

            // calcula as raizes para : x^2 + 5x + 6 usando a implementação das instâncias dos delegates
            double valor1 = raiz1(1d, 5d, 6d);
            double valor2 = raiz2(1d, 5d, 6d);
            //resultado
            Console.WriteLine("Para a equação:  x^2 + 5x + 6 : As raízes são {0}, {1}", valor1, valor2);
            // calcula as raizes para : x^2 + 10x + 24
            double valor3 = raiz1(1d, -10d, 24d);
            double valor4 = raiz2(1d, -10d, 24d);
            // resultado
            Console.WriteLine("Para a equação : x^2 - 10x + 24 : As raízes são {0}, {1}", valor3, valor4);
            // calcula as raizes para : x^2 + 8x + 16
            double valor5 = raiz1(1d, 8d, 16d);
            double valor6 = raiz2(1d, 8d, 16d);
            // resultado
            Console.WriteLine("Para a equação : x^2 + 8x + 16 : As raízes são {0}, {1}", valor5, valor6);
            Console.ReadLine();
        }

Se você refatorar o código acima, vai chegar exatamente ao código que foi implementado na primeira parte deste artigo, mostrando que as expressões lambda nada mais são que delegates anônimos.

A seguir, temo o código completo da implementação e o resultado da sua execução:

using System;
namespace Expressaolambda
{
    class Program
    {
        delegate double EquacaoSegundoGrauDelegate(double a, double b, double c);
        static void Main(string[] args)
        {
            Console.WriteLine("------------------------------------------------------------------------");
            Console.WriteLine("Cálculo das raízes da equação do segundo grau pela fórmula de Bhaskara");
            Console.WriteLine("Usando delegates e expressões lambdas");
            Console.WriteLine("------------------------------------------------------------------------");
            Metodo2();
            Console.WriteLine("------------------------------------------------------------------------");
            Console.ReadKey();
        }
        static void Metodo2()
        {
            Console.WriteLine("Método #2");
            // cria um método anônimo para calcular o discriminante
            EquacaoSegundoGrauDelegate discriminante = (a,b,c) => (b * b - 4d * a * c); 
            // cria métodos anônimos para calcular as raízes da equação
            EquacaoSegundoGrauDelegate raiz1 = (a,b,c) => (-b + Math.Sqrt(discriminante(a, b, c))) / (2 * a); 
            EquacaoSegundoGrauDelegate raiz2 = (a,b,c) => (-b - Math.Sqrt(discriminante(a, b, c))) / (2 * a); 

            // calcula as raizes para : x^2 + 5x + 6 usando a implementação das instâncias dos delegates
            double valor1 = raiz1(1d, 5d, 6d);
            double valor2 = raiz2(1d, 5d, 6d);
            //resultado
            Console.WriteLine("Para a equação:  x^2 + 5x + 6 : As raízes são {0}, {1}", valor1, valor2);
            // calcula as raizes para : x^2 + 10x + 24
            double valor3 = raiz1(1d, -10d, 24d);
            double valor4 = raiz2(1d, -10d, 24d);
            // resultado
            Console.WriteLine("Para a equação : x^2 - 10x + 24 : As raízes são {0}, {1}", valor3, valor4);
            // calcula as raizes para : x^2 + 8x + 16
            double valor5 = raiz1(1d, 8d, 16d);
            double valor6 = raiz2(1d, 8d, 16d);
            // resultado
            Console.WriteLine("Para a equação : x^2 + 8x + 16 : As raízes são {0}, {1}", valor5, valor6);
            Console.ReadLine();
        }
    }
}

Resultado obtido pela execução do código acima:

c_lambdel21

Podemos melhorar o nosso exemplo usando o delegate Func, que já está implementado na plataforma .NET e faremos isso na terceira parte do artigo. Aguarde…

Pegue o exemplo do projeto aqui: EquacaoSegundoGrau_Delegate_Lambda.zip