# Desmistificando Covariância e Contravariância em C#

Se existe uma funcionalidade pouco explorada no ambiente .NET é a de generalizar *interfaces* ou *delegates* utilizando de covariância e contravariância.

O que é?
--------

Esse recurso foi adicionado no C# 4 e está intimamente ligada na forma que tratamos tipos genéricos, vamos partir de exemplos para  ajudar a demonstrar o problema que esse recurso veio resolver.

Imagine que temos uma classe `Banana` que herde de uma classe `Fruta`, podemos ter em algum lugar um método simples que receba um `IEnumerable<Fruta>`, itere sobre ele e exiba seus nomes.

Primeiro vamos definir a classe base:

```csharp
public class Fruta
{
    public string Nome { get; set; }

    public Fruta(string nome)
    {
        Nome = nome;
    }
}
```

E no programa principal um método que exibe os nomes de qualquer coleção de frutas:

```csharp
public static void ExibirFrutas(IEnumerable<Fruta> frutas)
{
    foreach (var fruta in frutas)
        Console.WriteLine($"A fruta é: {fruta.Nome}");
}
```

A partir desse ponto podemos definir uma coleção de frutas e chamar nosso método `ExibirFrutas`

```csharp
public static void Main() {
    var frutas = new[] {
        new Fruta("Banana"),
        new Fruta("Maçã"),
        new Fruta("Tomate"),
    };
    ExibirFrutas(frutas);
}
```

Até aqui tudo bem, compila e funciona da forma que esperamos, sem surpresas.

Vamos agora fazer uma pequena mudança, primeiro criar uma classe derivada de fruta:

```csharp
public class Banana : Fruta
{
    public Banana(string nome) : base(nome){}
}
```

E vamos substituir a criação da coleção que irá ser usada para invocar o método `ExibirFrutas`

```csharp
public static void Main() {
    var frutas = new[] {
        new Banana("Banana Maçã"),
        new Banana("Banana Nanica"),
        new Banana("Banana Prata"),
    };
    ExibirFrutas(frutas);
}
```

Note que não alteramos a assinatura do método `ExibirFrutas`, continua sendo `void ExibirFrutas(IEnumerable<Fruta> frutas)`, nota-se aqui que o que estamos recebendo é um enumerado com tipo genérico de uma classe mais baixa em hierarquia, esse é o comportamento padrão quando tratamos de classes bases e derivadas, ou seja, posso usar classes derivadas aonde se espera classes bases.

Porém nesse exemplo é um pouco diferente, pois estamos tratando de parâmetros genéricos, nossas classes estão envelopadas em um `IEnumerable<T>`, de fato antes do C# 4, esta mudança que fizemos implicaria que nosso código não iria nem compilar, reclamando que o uso de `IEnumerable<Banana>` não poderia ser utilizado no lugar de `IEnumerable<Fruta>` nesta versão.

Esse código no C# moderno funciona sem problema, isso se dá ao fato da interface `IEnumerable` ser covariante, o que permite que eu utilize tipos derivados do tipo genérico que utilizei para definir meu `IEnumerable`, assim a assinatura atual do mesmo é:

```csharp
public interface IEnumerable<out T> { ... }

```

O que diz ao compilador que esse comportamento é desejado é essa keyword out, ela define um tipo genérico em uma interface ou delegate como covariante.

Qual a diferença entre covariância e contravariância?
-----------------------------------------------------

Vamos falar de teoria dos conjuntos, mas nada muito complexo, covariância e contravariância são propriedades de uma função de mapeamento que associa um membro de um conjunto a outro.

Considere os dois subconjuntos seguintes do conjunto de todos os tipos do C#.

Primeiro:

```csharp
{ Animal,
Tigre,
Fruta,
Banana }
```

Segundo:

```csharp
{ IEnumerable<Animal>,
  IEnumerable<Tigre>,
  IEnumerable<Fruta>,
  IEnumerable<Banana> }
```

Há claramente uma operação de mapeamento do primeiro conjunto para o segundo conjunto. Sendo que para cada `T` no primeiro conjunto tem um tipo correspondente no segundo conjunto que é `IEnumerable<T>`. resumindo, o mapeamento é `T → IEnumerable<T>`. (observe que esta (→) é uma "seta magra" que indica mapeamento).

Agora vamos considerar as relações. Existe uma relação de compatibilidade de atribuição entre pares de tipos no primeiro conjunto. O que simplificando significa que posso usar alguns tipo no lugar de outros (por causa da herança).

Um valor do tipo Tigre pode ser atribuído a uma variável do tipo Animal, portanto, esses tipos são considerados "compatíveis em atribuição".\
Vamos escrever "um valor do tipo X pode ser atribuído a uma variável do tipo Y" da seguinte forma `X ⇒ Y`. (note que esta (⇒) é uma "seta gorda" que significa compatibilidade em atribuição).

Então, em nosso primeiro subconjunto, aqui estão todos os relacionamentos de compatibilidade de atribuição possíveis:

```csharp
Tigre ⇒ Tigre
Tigre ⇒ Animal
Animal ⇒ Animal
Banana ⇒ Banana
Banana ⇒ Fruta
Fruta ⇒ Fruta
```

Desde o C# 4, é suportada a compatibilidade de atribuição covariante de certas interfaces, sendo assim existe uma relação de compatibilidade entre pares de tipos no segundo conjunto também:

```csharp
IEnumerable<Tigre>  ⇒ IEnumerable<Tigre>
IEnumerable<Tigre>  ⇒ IEnumerable<Animal>
IEnumerable<Animal> ⇒ IEnumerable<Animal>
IEnumerable<Banana> ⇒ IEnumerable<Banana>
IEnumerable<Banana> ⇒ IEnumerable<Fruta>
IEnumerable<Fruta>  ⇒ IEnumerable<Fruta>
```

Observe que o mapeamento `T → IEnumerable<T>` preserva a existência e a direção da compatibilidade da atribuição.\
Isto é, se `X ⇒ Y`, então também é verdade que `IEnumerable<X> ⇒ IEnumerable<Y>`.

Um mapeamento que possui essa propriedade em relação a uma determinada relação é chamado de "mapeamento covariante".

Isso deve fazer sentido: uma sequência de tigres pode ser usada onde é necessario uma sequência de animais, mas o oposto não é verdade.

Isso é covariância.

Agora vamos entender contravariância. Considere este subconjunto do conjunto de todos os tipos do C#:

```csharp
{ IComparable<Tigre>,
  IComparable<Animal>,
  IComparable<Fruta>,
  IComparable<Banana> }
```

Agora temos o mapeamento do primeiro conjunto para um terceiro conjunto `T → IComparable`.

```csharp
IComparable<Tigre> ⇒ IComparable<Tigre>
IComparable<Animal> ⇒ IComparable<Tigre> // invertido!
IComparable<Animal> ⇒ IComparable<Animal>
IComparable<Banana> ⇒ IComparable<Banana>
IComparable<Fruta> ⇒ IComparable<Banana>  // invertido!
IComparable<Fruta> ⇒ IComparable<Fruta>
```

Ou seja, o mapeamento `T → IComparable<T>` preservou a existência, mas inverteu a direção da compatibilidade da atribuição. Isto é, se `X ⇒ Y`, então `IComparable<X> ⇐ IComparable<Y>`.

Um mapeamento que preserva mas inverte uma relação é chamado de "mapeamento contravariante".

Sendo assim, fica claro que algo que pode comparar dois animais também pode comparar dois tigres, mas algo que pode comparar dois tigres não pode necessariamente comparar dois animais.

Diferente do `IEnumerable` a assinatura do `IComparable` utiliza a keyword `in` em seu parâmetro genérico.

```csharp
public interface IComparable<in T> { ... }
```

Essa é a diferença entre covariância e contravariância. Covariância preserva a direção de atribuição e a contravariância a inverte.

Por que "in" e "out"?
---------------------

Vamos olhar novamente, agora de forma completa as interfaces:

```csharp
public interface IEnumerable<out T>
{
    IEnumerator<T> GetEnumerator();
}
```


e

```csharp
public interface IComparable<in T>
{
    int CompareTo(T other);
}
```


Note que no `IEnumerable` seu tipo genérico está sendo utilizado apenas como saída, por isso a keyword `out`, e no `IComparable` aonde é aplicado a keyword `in` o tipo genérico é utilizado apenas para entrada, é exatamente essa a semântica do 'in' e 'out' respectivamente para contravariância e covariância.

Exemplos de Covariância: `IEnumerable<out T>`, `Func<out T>`\
Você pode converter de `IEnumerable<string>` para `IEnumerable<object>`, ou `Func<string>` para `Func<object>`. Isso por que os valores só saem desses objetos. Isso funciona porque, se você está tirando valores da API e retornando algo específico (como string), pode tratar esses valores retornados como um tipo mais geral (como objeto).

Exemplos Contravariância: `IComparer<in T>`, `Action<in T>`\
Você pode converter de `IComparer<object>` para `IComparer<string>` ou `Action<object>` para `Action<string>`, os valores só entram nesses objetos.\
Desta vez funciona porque se a API sabe tratar algo geral (como objeto) você pode passar algo mais específico (como string) que ela também saberá.

De forma geral, se você tiver uma interface `IFoo<T>`, ela pode ser covariante em T (isto é, declarar como `IFoo<out T>` se T for usado apenas como saida (por exemplo, um tipo de retorno) dentro da interface. E ser contravariante em T (isto é, `IFoo<in T>`) se T for usado apenas em uma posição de entrada (por exemplo, um tipo de parâmetro).

Vale ressaltar que um tipo não pode ser ao mesmo tempo covariante e contravariante, isso se dá pelo motivo de que o compilador irá se perder no tipo genérico e causar certas situações bizarras como:

```csharp
public interface IFoo<in out T> // absurdo!
{
    T Bar(T baz);
}

public class Foo<T> : IFoo<T>
{
   public T Value;
   T Bar(T baz);
}

public static void Main()
{
    var a = new Foo<string>();
    IFoo<object> b = a; //covariância
    IFoo<int[]> c = b; // contravariância

    Console.WrileLine(c.Value); // ?????
}
```


Qual o valor que deveria ser printado? Não dá para saber, por isso essas duas propriedades não podem existir mutuamente.

Conclusão
---------

Covariância e Contravariância são ferramentas poderosas e pouco exploradas, sempre devemos considera-las quando estamos trabalhando com tipo genéricos.
