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)