Delegados fortemente tipados
No artigo anterior, você viu que cria tipos de delegados específicos usando a delegate
palavra-chave.
A classe abstrata Delegate fornece a infraestrutura para acoplamento solto e invocação. Os tipos Concrete Delegate tornam-se muito mais úteis ao abraçar e impor a segurança de tipo para os métodos que são adicionados à lista de invocação para um objeto delegado. Quando você usa a delegate
palavra-chave e define um tipo de delegado concreto, o compilador gera esses métodos.
Na prática, isso levaria à criação de novos tipos de delegados sempre que você precisar de uma assinatura de método diferente. Este trabalho pode tornar-se tedioso depois de um tempo. Cada novo recurso requer novos tipos de delegados.
Felizmente, isso não é necessário. A estrutura do .NET Core contém vários tipos que você pode reutilizar sempre que precisar de tipos delegados. Essas são definições genéricas para que você possa declarar personalizações quando precisar de novas declarações de método.
O primeiro desses tipos é o Action tipo, e várias variações:
public delegate void Action();
public delegate void Action<in T>(T arg);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
// Other variations removed for brevity.
O in
modificador no argumento de tipo genérico é abordado no artigo sobre covariância.
Existem variações do Action
delegado que contêm até 16 argumentos, como Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>.
É importante que essas definições usem argumentos genéricos diferentes para cada um dos argumentos delegados: Isso lhe dá a máxima flexibilidade. Os argumentos do método não precisam ser, mas podem ser, do mesmo tipo.
Use um dos Action
tipos para qualquer tipo de delegado que tenha um tipo de retorno vazio.
A estrutura também inclui vários tipos de delegados genéricos que você pode usar para tipos delegados que retornam valores:
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T1, out TResult>(T1 arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
// Other variations removed for brevity
O out
modificador no argumento de tipo genérico de resultado é abordado no artigo sobre covariância.
Há variações do Func
delegado com até 16 argumentos de entrada, como Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>.
O tipo do resultado é sempre o último parâmetro de tipo em todas as Func
declarações, por convenção.
Use um dos Func
tipos para qualquer tipo de delegado que retorne um valor.
Há também um tipo especializado Predicate<T> para um delegado que retorna um teste em um único valor:
public delegate bool Predicate<in T>(T obj);
Você pode notar que, para qualquer Predicate
tipo, existe um tipo estruturalmente equivalente Func
Por exemplo:
Func<string, bool> TestForString;
Predicate<string> AnotherTestForString;
Você pode pensar que esses dois tipos são equivalentes. Tal não acontece. Estas duas variáveis não podem ser utilizadas indistintamente. Uma variável de um tipo não pode ser atribuída ao outro tipo. O sistema de tipo C# usa os nomes dos tipos definidos, não a estrutura.
Todas essas definições de tipo de delegado na Biblioteca Principal do .NET devem significar que você não precisa definir um novo tipo de delegado para qualquer novo recurso criado que exija delegados. Essas definições genéricas devem fornecer todos os tipos de delegados necessários na maioria das situações. Você pode simplesmente instanciar um desses tipos com os parâmetros de tipo necessários. No caso de algoritmos que podem ser tornados genéricos, esses delegados podem ser usados como tipos genéricos.
Isso deve economizar tempo e minimizar o número de novos tipos que você precisa criar para trabalhar com delegados.
No próximo artigo, você verá vários padrões comuns para trabalhar com delegados na prática.