C# - Usando expressões lambdas para implementar delegates - II


 Neste artigo eu vou continuar o artigo anterior e a 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 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:

y => y * y   
    Function(y) y * y   
Na linguagem C# todas as expressões usam o operador lambada => que significa "vá para"
C# VB .NET  

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 lambadas 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 lambdas 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 lambdas.

 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 3 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 assim que as expressões lambdas 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:

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

Pegue o exemplo do projeto aqui:  EquacaoSegundoGrau_Delegate_Lambda.zip

Porque eu, pela lei, estou morto para a lei, para viver para Deus.
Já estou crucificado com Cristo; e vivo, não mais eu, mas Cristo vive em mim; e a vida que agora vivo na carne, vivo-a pela fé do Filho de Deus, o qual me amou, e se entregou a si mesmo por mim.

Gálatas 2:19-20

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?


             Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter
 

Referências:


José Carlos Macoratti