Variância em interfaces genéricas (C#)
O .NET Framework 4 introduziu o suporte à variância para várias interfaces genéricas existentes. O suporte à variância permite a conversão implícita de classes que implementam essas interfaces.
A partir do .NET Framework 4, as seguintes interfaces são variantes:
IEnumerable<T> (T é covariante)
IEnumerator<T> (T é covariante)
IQueryable<T> (T é covariante)
IGrouping<TKey,TElement>
TKey
( e são covariantesTElement
)IComparer<T> (T é contravariante)
IEqualityComparer<T> (T é contravariante)
IComparable<T> (T é contravariante)
A partir do .NET Framework 4.5, as seguintes interfaces são variantes:
IReadOnlyList<T> (T é covariante)
IReadOnlyCollection<T> (T é covariante)
A covariância permite que um método tenha um tipo de retorno mais derivado do que o definido 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 IEnumerable<String>
interface não herda a IEnumerable<Object>
interface. No entanto, o String
tipo herda o Object
tipo e, em alguns casos, você pode querer 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 causa um erro de compilação em C# e, se Option Strict
estiver ativado, no Visual Basic. Mas agora você pode usar strings
em vez de objects
, como mostrado no exemplo anterior, porque a IEnumerable<T> interface é covariante.
A contravariância permite que um método tenha tipos de argumento que são menos derivados do que o especificado pelo parâmetro genérico da interface. Para ilustrar a contravariância, suponha que você criou uma BaseComparer
classe para comparar instâncias da BaseClass
classe. A classe BaseComparer
implementa a interface IEqualityComparer<BaseClass>
. Como a IEqualityComparer<T> interface agora é contravariante, você pode usar BaseComparer
para comparar instâncias de classes que herdam a BaseClass
classe. 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 obter mais exemplos, consulte Usando variância em interfaces para coleções genéricas (C#).
A variância em interfaces genéricas é suportada apenas para tipos de referência. Os tipos de valor não suportam variância. 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 IEnumerable<T>covariante, você não pode converter List<String>
implicitamente em 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>();