Variância em interfaces genéricas (C#)

O .NET Framework 4 introduziu o suporte à variação para diversas interfaces genéricas existentes. O suporte à variação possibilita a conversão implícita de classes que implementam essas interfaces.

Do .NET Framework 4 em diante, as seguintes interfaces são variantes:

A partir do .NET Framework 4.5, as seguintes interfaces são variantes:

A covariância permite que um método tenha um tipo de retorno mais derivados que aquele definidos pelo parâmetro de tipo genérico da interface. Para ilustrar o recurso de covariância, considere estas interfaces genéricas: IEnumerable<Object> e IEnumerable<String>. A interface IEnumerable<String> não herda a interface IEnumerable<Object>. No entanto, o tipo String herda o tipo Object e, em alguns casos, talvez você queira atribuir objetos dessas interfaces uns aos outros. Isso é mostrado no exemplo de código a seguir.

IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

Em versões anteriores do .NET Framework, esse código gera um erro de compilação no C# e, se Option Strict estiver ativado, no Visual Basic. Mas agora você pode usar strings em vez de objects, conforme mostrado no exemplo anterior, porque a interface IEnumerable<T> é covariante.

A contravariância permite que um método tenha tipos de argumentos menos derivados que aquele especificado pelo parâmetro genérico da interface. Para ilustrar a contravariância, suponha que você tenha criado uma classe BaseComparer para comparar instâncias da classe BaseClass. A classe BaseComparer implementa a interface IEqualityComparer<BaseClass>. Como a interface IEqualityComparer<T> agora é contravariante, você pode usar BaseComparer para comparar instâncias de classes que herdam a classe BaseClass. Isso é mostrado no exemplo de código a seguir.

// Simple hierarchy of classes.
class BaseClass { }
class DerivedClass : BaseClass { }

// Comparer class.
class BaseComparer : IEqualityComparer<BaseClass>
{
    public int GetHashCode(BaseClass baseInstance)
    {
        return baseInstance.GetHashCode();
    }
    public bool Equals(BaseClass x, BaseClass y)
    {
        return x == y;
    }
}
class Program
{
    static void Test()
    {
        IEqualityComparer<BaseClass> baseComparer = new BaseComparer();

        // Implicit conversion of IEqualityComparer<BaseClass> to
        // IEqualityComparer<DerivedClass>.
        IEqualityComparer<DerivedClass> childComparer = baseComparer;
    }
}

Para ver mais exemplos, consulte Usando variação em interfaces para coleções genéricas (C#).

A variação em interfaces genéricas tem suporte somente para tipos de referência. Tipos de valor não dão suporte à variação. Por exemplo, IEnumerable<int> não pode ser convertido implicitamente em IEnumerable<object>, porque inteiros são representados por um tipo de valor.

IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;

Também é importante lembrar que as classes que implementam interfaces variantes ainda são invariantes. Por exemplo, embora List<T> implemente a interface covariante IEnumerable<T>, você não pode converter implicitamente List<String> para List<Object>. Isso é ilustrado no exemplo de código a seguir.

// The following line generates a compiler error
// because classes are invariant.
// List<Object> list = new List<String>();

// You can use the interface object instead.
IEnumerable<Object> listObjects = new List<String>();

Confira também