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:

A partire da .NET Framework 4.5, le interfacce seguenti sono varianti:

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>();

Vedi anche