Вариативность в универсальных интерфейсах (Visual Basic)

В платформе .NET Framework 4 появилась поддержка вариативности для нескольких существующих универсальных интерфейсов. Поддержка вариативности позволяет выполнять неявное преобразование классов, реализующих эти интерфейсы. Сейчас вариативными являются следующие интерфейсы.

Ковариация позволяет методу иметь тип возвращаемого значения, степень наследования которого больше, чем указано в параметре универсального типа интерфейса. Чтобы продемонстрировать функцию ковариации, рассмотрим следующие универсальные интерфейсы: IEnumerable(Of Object) и IEnumerable(Of String). Интерфейс IEnumerable(Of String) не наследует интерфейс IEnumerable(Of Object). При этом тип String наследует тип Object, и в некоторых случаях может потребоваться назначить объекты этих интерфейсов друг другу. Это показано в следующем примере кода.

Dim strings As IEnumerable(Of String) = New List(Of String)
Dim objects As IEnumerable(Of Object) = strings

В более ранних версиях платформа .NET Framework этот код вызывает ошибку компиляции в Visual Basic.Option Strict On Но теперь можно использовать strings вместо objects, как показано в предыдущем примере, поскольку интерфейс IEnumerable<T> является ковариантным.

Контравариантность позволяет методу иметь типы аргументов, степень наследования которых меньше, чем указано в параметре универсального типа интерфейса. Чтобы продемонстрировать функцию контравариантности, предположим, что создан класса BaseComparer для сравнения экземпляров класса BaseClass. Класс BaseComparer реализует интерфейс IEqualityComparer(Of BaseClass). Поскольку теперь интерфейс IEqualityComparer<T> является контравариантным, для сравнения экземпляров классов, наследующих класс BaseClass, можно использовать класс BaseComparer. Это показано в следующем примере кода.

' 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

Дополнительные примеры см. в разделе "Использование дисперсии в интерфейсах для универсальных коллекций (Visual Basic)".

Вариативность в универсальных интерфейсах поддерживается только для ссылочных типов. Типы значений не поддерживают вариативность. Например, IEnumerable(Of Integer) нельзя неявно преобразовать в IEnumerable(Of Object), так как типом значения является integer.

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

Кроме того, важно помнить, что классы, которые реализуют вариативные интерфейсы, сами являются инвариантными. Например, несмотря на то, что List<T> реализует ковариантный интерфейс IEnumerable<T>, неявно преобразовать List(Of Object) в List(Of String) нельзя. Это показано в следующем примере кода.

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

См. также