DEV Community

Diogo Kobbi
Diogo Kobbi

Posted on

2 2

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!

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)