Varianza nelle interfacce generiche (C#)
In .NET framework 4 è stato introdotto il supporto della varianza per diverse interfacce generiche esistenti. Il supporto della varianza consente la conversione implicita delle classi che implementano tali interfacce.
A partire da .NET Framework 4, le interfacce seguenti sono varianti:
IEnumerable<T> (T è covariante)
IEnumerator<T> (T è covariante)
IQueryable<T> (T è covariante)
IGrouping<TKey,TElement> (
TKey
eTElement
sono covarianti)IComparer<T> (T è controvariante)
IEqualityComparer<T> (T è controvariante)
IComparable<T> (T è controvariante)
A partire da .NET Framework 4.5, le interfacce seguenti sono varianti:
IReadOnlyList<T> (T è covariante)
IReadOnlyCollection<T> (T è covariante)
La covarianza consente a un metodo di avere un tipo restituito più derivato rispetto a quello definito dal parametro di tipo generico dell'interfaccia. Per illustrare la funzionalità di covarianza, considerare le seguenti interfacce generiche: IEnumerable<Object>
e IEnumerable<String>
. L'interfaccia IEnumerable<String>
non eredita l'interfacciaIEnumerable<Object>
. Tuttavia, il tipo String
eredita il tipo Object
e in alcuni casi è opportuno assegnare gli oggetti di ogni interfaccia all'altra. Vedere il codice di esempio seguente.
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
Nelle versioni precedenti di .NET Framework, questo codice causa un errore di compilazione in C# e, con Option Strict
, in Visual Basic. Ora è invece possibile usare strings
anziché objects
, come illustrato nell'esempio precedente, poiché l'interfaccia IEnumerable<T> è covariante.
La controvarianza consente a un metodo di avere tipi di argomenti meno derivati rispetto a quelli specificati dal parametro generico dell'interfaccia. Per illustrare la controvarianza, si supponga di aver creato una classe BaseComparer
per confrontare le istanze della classe BaseClass
. La classe BaseComparer
implementa l'interfaccia IEqualityComparer<BaseClass>
. Poiché l'interfaccia BaseClass
è ora controvariante, è possibile usare IEqualityComparer<T> per confrontare le istanze delle classi che ereditano la classe BaseComparer
. Vedere il codice di esempio seguente.
// 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;
}
}
Per altri esempi, vedere Uso della varianza nelle interfacce per le raccolte generiche (C#).
La varianza nelle interfacce generiche è supportata solo per i tipi di riferimento. I tipi di valore non supportano la varianza. Ad esempio, non è possibile convertire IEnumerable<int>
in modo implicito in IEnumerable<object>
perché i valori integer sono rappresentati da un tipo di valore.
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;
È anche importante ricordare che le classi che implementano le interfacce varianti sono comunque invariabili. Ad esempio, sebbene List<T> implementi l'interfaccia covariante IEnumerable<T>, non è possibile convertire List<String>
in List<Object>
in modo implicito. come illustra l'esempio di codice riportato di seguito.
// 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>();