DEV Community

Diogo Kobbi
Diogo Kobbi

Posted on

C# - Delegates (Parte 1)

De modo simplificado, podemos considerar delegates como objetos que sabem invocar métodos - ou métodos que são tratados como objetos, como preferir. É um tipo que representa uma referência a métodos com determinado tipo de retorno e parâmetros.

Quando criamos uma instância de um delegate podemos associá-lo a qualquer método com assinatura compatível (retorno e parâmetros) e executá-lo mais tarde em nosso código.

Como criar e executar um delegate

A declaração de um tipo delegate é similar a assinatura de um método. Ela tem um tipo de retorno e um ou mais parâmetros de diversos tipos:

delegate Identificador ([parâmetros])
Após a criação, basta executar* o método usando o nome do objeto ou o método invoke:

delegate double CalculoMatematico(float valor1, float valor2);

public static class Calculadora {

   public static double Somar(float valor1, float valor2) => valor1 + valor2;

}

CalculoMatematico soma = Calculadora.Somar;

var resultado1 = soma.Invoke(2, 3);

var resultado2 = soma(3, 4);

*Importante: em um projeto real é melhor verificar se o objeto é nulo antes de invocá-lo.

Action e Func

Embora exista a possibilidade, nem sempre precisamos criar nossos próprios delegates. Também podemos utilizar os delegates Action e Func para agilizar nosso desenvolvimento.

Em vez de especificarmos a assinatura do delegate para depois utilizá-lo como tipo:

delegate doubleCalculoMatematico(float valor1, float valor2);

CalculoMatematico soma = Calculadora.Somar;

Podemos especificar na própria atribuição da variável:

Func<float, float, double> soma = Calculadora.Somar;

A diferença entre Action e Func é que um Action não retornará um valor, enquanto Func retornará um valor do tipo definido na declaração - ao declarar um Func, o último argumento sempre indicará o tipo de retorno (no exemplo acima, double será o tipo do retorno).

Qual forma é melhor? Depende. Criar delegates nos permite controlar melhor o nosso código, pois o nome que damos ao delegate nos ajuda a entender sua função. As duas formas tem seus prós e contras.

Para que serve um delegate?

Um uso muito comum é utilizar delegates em métodos de callback - um método de callback é um pedaço de código executável que é passado como parâmetro para algum método, é esperado que o método execute o código do argumento em algum momento. Veja, por exemplo, a declaração do método Where da Classe Enumerable:

Where<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>)  
//Filtra uma sequência de valores baseado em um predicado

Este método recebe um delegate (Func) como parâmetro que retornará um valor do tipo falso ou verdadeiro, o que significa que podemos utilizá-lo da seguinte maneira:

bool SaldoNegativo(float saldo) {

  return saldo < 0;

}

var contas = new List<ContaCorrente>();
contas.Add(...);
var clientesUsandoChequeEspecial = contas.Where(SaldoNegativo);

Ou criando uma função anônima (assunto para outro artigo) que tenha a mesma assinatura:

var clientesUsandoChequeEspecial = contas.Where(x => x.Saldo < 0);

Veja que atribuir um método a uma variável e utilizá-la em outros trechos do código nos ajuda a manter o nosso código fortemente tipado, pois forçamos o uso da mesma assinatura e evitamos que um método totalmente fora do propósito seja executado. Afinal, se não obrigássemos que uma função específica fosse utilizada, outros desenvolvedores poderiam passar delegates incompatíveis e o filtro do método where não funcionaria corretamente, não é mesmo?

No próximo artigo falarei sobre outros temas como Covariância, Multicast e Eventos, outro uso comum de delegates.

Até mais!

Top comments (0)