Condividi tramite


Varianza nelle interfacce generiche (C#)

.NET Framework 4 ha introdotto il supporto della varianza per diverse interfacce generiche esistenti. Il supporto della varianza consente la conversione implicita di classi che implementano queste 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 queste interfacce generiche: IEnumerable<Object> e IEnumerable<String>. L'interfaccia IEnumerable<String> non eredita l'interfaccia IEnumerable<Object> . Tuttavia, il String tipo eredita il Object tipo e, in alcuni casi, è possibile assegnare oggetti di queste interfacce l'uno all'altro. Questo è illustrato nell'esempio di codice 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, se Option Strict è attivo, in Visual Basic. Ma ora è possibile usare strings invece di objects, come illustrato nell'esempio precedente, perché l'interfaccia IEnumerable<T> è covariante.

La controvarianza consente a un metodo di avere tipi di argomento meno derivati rispetto a quelli specificati dal parametro generico dell'interfaccia. Per illustrare la controvarianza, si supponga di aver creato una BaseComparer classe per confrontare le istanze della BaseClass classe . La classe BaseComparer implementa l'interfaccia IEqualityComparer<BaseClass>. Poiché l'interfaccia IEqualityComparer<T> è ora controvariante, è possibile usare BaseComparer per confrontare le istanze di classi che ereditano la BaseClass classe . Questo è illustrato nell'esempio di codice 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 valore non supportano la varianza. Ad esempio, IEnumerable<int> non può essere convertito in modo implicito in IEnumerable<object>perché i numeri interi 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 interfacce varianti sono ancora invarianti. Ad esempio, anche se List<T> implementa l'interfaccia IEnumerable<T>covariante , non è possibile convertire List<String> in modo implicito in List<Object>. Questo è illustrato nell'esempio di codice seguente.

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

Vedere anche