C# - Delegates (Parte 1)
Tags: csharp, dotnet, delegates
Published: 2020-08-18 12:22:40

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 <tipo-de-retorno> Identificador ([parâmetros])
Após a criação, basta executar* o método usando o nome do objeto ou o método invoke:

```csharp
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:

```csharp 
delegate doubleCalculoMatematico(float valor1, float valor2);

CalculoMatematico soma = Calculadora.Somar;
```

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

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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!