Covariância e Contravariância em C#, Parte Três: Variância em Grupo de Métodos

02/13/2008 08:45:00 By Felipe Pessoto

Anteriormente discutimos como a covariância numa array não funciona corretamente no C# (e Java, assim como uma série de outras linguagens). Agora, um tipo de variância válida suportada no C# 2.0: conversões de grupo de métodos em delegates. Este é um tipo mais complicado de variância, por isso vou explicar com mais detalhes.

Suponha que você tem um método que retorna um objeto Girafa.

static Girafa CriarGirafa() { }

E que você tem um delegate representando uma função que não recebe argumentos e retorna um Animal. Isto é, Func<animal>. Deveria esta conversão ser válida?

Func<animal> func = CriarGirafa;

Ao invocar Func é esperado que um Animal seja retornado. A função atual chamada pelo delegate sempre retorna uma Girafa, a qual é um Animal, então o invocador da função nunca recebe nada que ele não seja capaz de lidar. Não há problemas no sistema de tipo aqui. Portanto nós podemos criar métodos para delegate usando conversões covariantes nos seus tipos de retorno.


Agora vamos supor que você tem dois métodos, um que recebe Girafa e um que recebe um Animal:

void Foo(Girafa g) { }
void Bar(Animal a) { }

e um delegate que retorna vazio e recebe um argumento do tipo Mamifero:

Action<Mamifero> action1 = Foo; // inválido
Action<Mamifero> action2 = Bar; // válido

Por quê a primeira atribuição é inválida? Porque quem invocar o action1 pode passar por exemplo um tipo Tigre(já que seria derivado de Mamifero), mas Foo não pode receber um Tigre, somente uma Girafa(e seus derivados)! A segunda atribuição é válida porque Bar pode receber qualquer Animal.

No exemplo anterior preservamos a direção de atribuição: Girafa é menor que Animal, então o método que retorna Girafa é menor que o delegate que retorna um Animal. Neste exemplo, nós revertemos a direção de atribuição: Mamifero é menor que Animal, então o método que recebe Animal é menor que o delegate que recebe um Mamifero. Porque a direção está invertida, conversões de grupo de métodos para delegate são contravariantes nos tipos de seus argumentos.

Percebe que tudo falado acima aplica-se apenas em tipos por referência. Nunca diga algo como "Bem, todo int cabe em um long, então um método que retorna um int pode ser atribuído para uma variável do tipo Func<long>".


Comments (0)