Compartir a través de


Varianza en interfaces genéricas (C# y Visual Basic)

.NET Framework 4 incluye compatibilidad con la varianza para varias interfaces genéricas ya existentes. La compatibilidad con la varianza habilita la conversión implícita de las clases que implementan estas interfaces. Las interfaces siguientes son ahora variantes:

La covarianza permite que un método tenga un tipo de valor devuelto más derivado que el definido por el parámetro de tipo genérico de la interfaz. Para explicar la característica de covarianza, considere estas interfaces genéricas: IEnumerable<Object> e IEnumerable<String> (IEnumerable(Of Object) e IEnumerable(Of String) en Visual Basic). La interfaz IEnumerable<String> (IEnumerable(Of String) en Visual Basic) no hereda la interfaz IEnumerable<Object> (IEnumerable(Of Object) en Visual Basic). Sin embargo, el tipo String sí hereda el tipo Object y, en algunos casos, puede que desee asignar objetos de estas interfaces entre sí. Esto se muestra en el ejemplo de código siguiente.

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;

En versiones anteriores de .NET Framework, este código produce un error de compilación en C# y en Visual Basic con Option Strict On. Ahora, sin embargo, puede usar objetos strings en lugar de objects, como se muestra en el ejemplo anterior, porque la interfaz IEnumerable<T> es covariante.

La contravarianza permite que un método tenga tipos de argumento menos derivados que el especificado por el parámetro genérico de la interfaz. Para explicar la contravarianza, suponga que se ha creado una clase BaseComparer para comparar las instancias de la clase BaseClass. La clase BaseComparer implementa la interfaz IEqualityComparer<BaseClass> (IEqualityComparer(Of BaseClass) en Visual Basic). Dado que la interfaz IEqualityComparer<T> es ahora contravariante, puede usar BaseComparer para comparar las instancias de las clases que heredan la clase BaseClass. Esto se muestra en el ejemplo de código siguiente.

' 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;
    }
}

Para obtener más ejemplos, vea Usar la varianza en interfaces para las colecciones genéricas (C# y Visual Basic).

La varianza en las interfaces genéricas solo es compatible con los tipos de referencia. Los tipos de valor no admiten la varianza. Por ejemplo, IEnumerable<int> (IEnumerable(Of Integer) en Visual Basic) no se puede convertir implícitamente en IEnumerable<object> (IEnumerable(Of Object) en Visual Basic) porque los enteros se representan mediante un tipo de valor.

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;

También es importante recordar que las clases que implementan interfaces variantes siguen siendo invariables. Por ejemplo, aunque List<T> implementa la interfaz de covariante IEnumerable<T>, no se puede convertir implícitamente List<Object> en List<String> (List(Of Object) en List(Of String) en Visual Basic). Esto se muestra en el siguiente ejemplo de código:

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

Vea también

Referencia

Usar la varianza en interfaces para las colecciones genéricas (C# y Visual Basic)

Conceptos

Crear interfaces genéricas variantes (C# y Visual Basic)

Interfaces genéricas

Varianza en delegados (C# y Visual Basic)