C# - Apresentando o Delegate Action


 Neste artigo eu vou apresentar os conceitos básicos envolvidos na utilização do delegado Action.

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 para fazer isso seria manter o tipo de parâmetro e o tipo de retorno.

Recursos usados :

Conceitos Básicos

1- O delegate Action

O Delegate Action encapsula um método que não retorna nenhum valor (void) e pode receber de zero a 16 parâmetros de entrada.

Sua assinatura possui 17 sobrecargas:  Action, Action<T>, Action<T1,T2>, .....Action<T1,.....,T16>

Sintaxe :

   public delegate void Action<in T>( T obj ) ( e mais 16 sobrecargas...)

Parâmetros de tipo : in T

 - Onde T é o tipo do parâmetro do método que encapsula este delegado. (Este parâmetro de tipo é contravariante. Ou seja, você pode usar o tipo especificado ou qualquer tipo que seja menos derivado)

Parâmetros : obj

- O parâmetro do método que encapsula este delegado;

Este delegate está contido no assembly mscorlib.dll namespace System.

Esse delegate  são usados em conjunto com Arrays e Listas, pois ele evita que você tenha que criar um método exclusivo para iterar pela respectiva coleção. A classe Array/List possui vários métodos que recebem como parâmetros delegates do tipo Action como veremos a seguir.

Então se você precisar de um delegate que não devolve nenhum valor pode usar o Action.

Exemplo :

using System;
namespace Action_Delegate
{
    class Program
    {
        static void Main(string[] args)
        {
            double n1 = 100;
            double n2 = 200;
            Action<double, double> dividir = Dividir;
            dividir(n1, n2);
        }
        static void Dividir(double num1, double num2)
        {
            double resultado = num1 / num2;
            Console.WriteLine(string.Format(" Divisão : {0} ", resultado));
        }
    }
}

Uma utilização muito frequente do delegate Action é quando precisamos realizar uma ação sobre itens de uma lista.

Podemos utilizar uma expressão lambda e simplificar o código da seguinte forma:

using System;
namespace Action_Delegate
{
    class Program
    {
        static void Main(string[] args)
        {
            double n1 = 100;
            double n2 = 200;
            Action<double, double> dividir = (x,y) => Console.WriteLine(string.Format(" Divisão : {0} ", x/y));
            dividir(n1, n2);
        }
    }
}

Exemplo:

Vamos definir uma array chamado lista contendo o nome de alguns times de futebol:

No código o método ForEach aceita um array e um delegate Action do tipo string.

Definimos a variável lista para o array e o método Console.Writeline para Action.

Isso esta correto porque a lista e o método Console.Writeline são do tipo String e também porque este método não retorna nada e respeita a assinatura do delegate Action.

Executando o projeto teremos o seguinte resultado:

Poderíamos também ter declarado o delegate Action diretamente e depois usá-lo no método ForEach:

using System;
namespace Action2
{
    class Program
    {
        static void Main(string[] args)
        {
            var lista = new[] { "Santos", "Palmeiras", "Vasco", "Bahia" };
            Action<string> minhaAction = new Action<string>(Console.WriteLine);
            Array.ForEach(lista, minhaAction);
            Console.ReadKey();
        }
    }
}

Vejamos um exemplo mais prático onde temos uma classe Funcionario que retorna uma lista de funcionários com o código abaixo:

using System;
using System.Collections.Generic;
namespace CSharp_Delegates_Action_Func_Predicate
{
    public class Funcionario
    {
        public string Nome { get; set; }
        public string SobreNome { get; set; }
        public DateTime Nascimento { get; set; }
        public int Idade { get; set; }
        public static List<Funcionario> GetFuncionarios()
        {
            return new List<Funcionario>()
        {
            new Funcionario()
            {
                Nome = "Jose Carlos",
                SobreNome = "Macoratti",
                Nascimento =  Convert.ToDateTime("11/09/1975")
            },
            new Funcionario()
            {
                Nome = "Jefferson",
                SobreNome = "Ribeiro",
                Nascimento = Convert.ToDateTime("20/08/1992")
            },
            new Funcionario()
            {
                Nome = "Janice",
                SobreNome = "Santos",
                Nascimento = Convert.ToDateTime("13/06/1990")
            }
        };
       }
    }
}

Para retornar uma lista de funcionários em uma variável do tipo List<T> usamos o código :

List<Funcionario> funcionarios = Funcionario.GetFuncionarios();

E para iterar sobre a lista de funcionários podemos usar o método ForEach de List<T> :

Observe que o método ForEach aceita um delegate Action do tipo T, onde no meu exemplo T representa o tipo Funcionario.

Vamos então definir uma Action.

Primeiro vamos criar um método chamado CalculaIdade(Funcionario funci) para calcular a idade do funcionário com o código abaixo:

  static void CalculaIdade(Funcionario funci)
  {
      funci.Idade = DateTime.Now.Year - funci.Nascimento.Year;
 }

 E a seguir vamos definir a Action apontando para o método acima e depois podemos exibir a idade calculada para cada funcionário usando o método ForEach :

  static void Main(string[] args)
  {
            List<Funcionario> funcionarios = Funcionario.GetFuncionarios();

            Action<Funcionario> minhaAction = new Action<Funcionario>(CalculaIdade);
            funcionarios.ForEach(minhaAction);

            foreach (Funcionario funci in funcionarios)
            {   
                Console.WriteLine(funci.Nome + " " + funci.Idade.ToString());
            }
            Console.ReadKey();
  }

Veja abaixo o código completo e o resultado da execução do projeto:

Podemos simplificar o nosso código usando uma expressão lambda de forma a eliminar o código do método CalculaIdade() :

Nota: Uma expressão lambda é uma função anônima, ou seja, um método sem uma declaração, sem modificador de acesso, sem declaração de valor de retorno e sem nome.
Elas pode conter instruções e expressões e podemos usá-las para criar delegates ou tipos de árvores de expressão.  O operador lambda  é identificado como " => " e significa 'vai para'.
Declaração : Parâmetros => Código;

funcionarios.ForEach(f => f.Idade = DateTime.Now.Year - f.Nascimento.Year);

Agora nosso código ficou assim:

using System;
using System.Collections.Generic;
namespace CSharp_Delegates_Action_Func_Predicate
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Funcionario> funcionarios = Funcionario.GetFuncionarios();

            //expressão lambada
            funcionarios.ForEach(f => f.Idade = DateTime.Now.Year - f.Nascimento.Year);
 
           foreach (Funcionario funci in funcionarios)
            {   
                Console.WriteLine(funci.Nome + " " + funci.Idade.ToString());
            }
            Console.ReadKey();
        }
    }
}

Assim o delegate Action pode ser usado quando não temos nenhum tipo de retorno do método.

Nota : Note que os métodos foreach e ForEach<T> usam um delegado Action<T> como parâmetro e o método encapsulado pelo delegado permite que você execute uma ação em cada elemento na matriz ou lista.

Pegue o exemplo do projeto aqui: CSharp_Delegates_Action.zip

O que era desde o princípio, o que ouvimos, o que vimos com os nossos olhos, o que temos contemplado, e as nossas mãos tocaram da Palavra da vida
(Porque a vida foi manifestada, e nós a vimos, e testificamos dela, e vos anunciamos a vida eterna, que estava com o Pai, e nos foi manifestada);
O que vimos e ouvimos, isso vos anunciamos, para que também tenhais comunhão conosco; e a nossa comunhão é com o Pai, e com seu Filho Jesus Cristo.
1 João 1:1-3


Referências:


José Carlos Macoratti