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:
IEnumerable<T> (T é covariante)
IEnumerator<T> (T é covariante)
IQueryable<T> (T é covariante)
IGrouping<TKey,TElement> (
TKey
eTElement
são covariantes)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 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>();