Varianza nelle interfacce generiche (C# e Visual Basic)

In .NET Framework 4 viene introdotto il supporto della varianza per diverse interfacce generiche esistenti. Il supporto della varianza abilita la conversione implicita di classi che implementano queste interfacce. Le interfacce seguenti sono ora variant:

La covarianza consente a un metodo di disporre di un tipo restituito più derivato di quello definito dal parametro di tipo generico dell'interfaccia. Per illustrare la funzionalità della covarianza, si considerino le seguenti interfacce generiche: IEnumerable<Object> e IEnumerable<String> (IEnumerable(Of Object) e IEnumerable(Of String) in Visual Basic). L'interfaccia IEnumerable<String> (IEnumerable(Of String) in Visual Basic) non eredita l'interfaccia IEnumerable<Object> (IEnumerable(Of Object) in Visual Basic). Il tipo String, tuttavia, eredita il tipo Object e in alcuni casi potrebbe essere necessario assegnare gli oggetti di queste interfacce l'uno all'altro. Questa operazione è illustrata nell'esempio di codice seguente.

Dim strings As IEnumerable(Of String) = New List(Of String)
Dim objects As IEnumerable(Of Object) = strings
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

Nelle versioni precedenti di .NET Framework, questo codice provoca un errore di compilazione in C# e in Visual Basic con Option Strict On. Ma ora è possibile utilizzare strings anziché objects, come illustrato nell'esempio precedente, perché l'interfaccia IEnumerable<T> è covariante.

La controvarianza consente a un metodo di disporre di tipi di argomento meno derivati di quello specificato dal parametro generico dell'interfaccia. Per illustrare la controvarianza, si supponga di avere creato una classe BaseComparer per confrontare le istanze della classe BaseClass. La classe BaseComparer implementa l'interfaccia IEqualityComparer<BaseClass> (IEqualityComparer(Of BaseClass) in Visual Basic). Poiché l'interfaccia IEqualityComparer<T> è ora controvariante, è possibile utilizzare BaseComparer per confrontare le istanze delle classi che ereditano la classe BaseClass. Questa operazione è illustrata nell'esempio di codice seguente.

' Simple hierarchy of classes.
Class BaseClass
End Class

Class DerivedClass
    Inherits BaseClass
End Class

' Comparer class.
Class BaseComparer
    Implements IEqualityComparer(Of BaseClass)

    Public Function Equals1(ByVal x As BaseClass,
                            ByVal y As BaseClass) As Boolean _
                            Implements IEqualityComparer(Of BaseClass).Equals
        Return (x.Equals(y))
    End Function

    Public Function GetHashCode1(ByVal obj As BaseClass) As Integer _
        Implements IEqualityComparer(Of BaseClass).GetHashCode
        Return obj.GetHashCode
    End Function
End Class
Sub Test()
    Dim baseComparer As IEqualityComparer(Of BaseClass) = New BaseComparer
    ' Implicit conversion of IEqualityComparer(Of BaseClass) to 
    ' IEqualityComparer(Of DerivedClass).
    Dim childComparer As IEqualityComparer(Of DerivedClass) = baseComparer
End Sub
// 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 ulteriori esempi, vedere Utilizzo della varianza nelle interfacce per gli insiemi generici (C# e Visual Basic).

La varianza nelle interfacce generiche è supportata solo per i tipi di riferimento. I tipi di valore non supportano la varianza. Non è possibile, ad esempio, convertire in modo implicito IEnumerable<int> (IEnumerable(Of Integer) in Visual Basic) in IEnumerable<object> (IEnumerable(Of Object) in Visual Basic), perché i valori interi sono rappresentati da un tipo di valore.

Dim integers As IEnumerable(Of Integer) = New List(Of Integer)
' The following statement generates a compiler error
' with Option Strict On, because Integer is a value type.
' Dim objects As IEnumerable(Of Object) = integers
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler errror,
// because int is a value type.
// IEnumerable<Object> objects = integers;

È inoltre importante ricordare che le classi che implementano le interfacce variant sono ancora invariabili. Anche se, ad esempio, List<T> implementa l'interfaccia covariante IEnumerable<T>, non è possibile convertire in modo implicito List<Object> in List<String> (List(Of Object) in List(Of String) in Visual Basic), come illustrato nell'esempio di codice riportato di seguito.

' The following statement generates a compiler error
' because classes are invariant.
' Dim list As List(Of Object) = New List(Of String)

' You can use the interface object instead.
Dim listObjects As IEnumerable(Of Object) = New List(Of String)
// 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

Riferimenti

Utilizzo della varianza nelle interfacce per gli insiemi generici (C# e Visual Basic)

Concetti

Creazione di interfacce generiche variant (C# e Visual Basic)

Interfacce generiche

Varianza nei delegati (C# e Visual Basic)