Поделиться через


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

Параметры универсального типа можно объявить в интерфейсах как ковариантный или контравариантный. Ковариация позволяет методам интерфейса иметь более производные типы возвращаемых значений, чем определенные параметрами универсального типа. Контравариантность позволяет методам интерфейса иметь типы аргументов, которые являются менее производными, чем указанные универсальными параметрами. Универсальный интерфейс с ковариантными или контравариантными параметрами универсального типа называется вариантом.

Замечание

Платформа .NET Framework 4 представила поддержку дисперсии для нескольких существующих универсальных интерфейсов. Список вариантов интерфейсов в .NET Framework см. в разделе "Вариативность" в универсальных интерфейсах (Visual Basic).

Объявление универсальных интерфейсов variant

Вы можете объявлять ковариантные и контравариантные универсальные интерфейсы с помощью ключевых слов in и out для параметров универсального типа.

Это важно

ByRef параметры в Visual Basic не могут быть вариантами. Типы значений также не поддерживают дисперсию.

Параметр универсального типа можно объявить ковариантным с помощью ключевого out слова. Ковариантный тип должен соответствовать следующим условиям:

  • Тип используется только в качестве возвращаемого типа методов интерфейса и не используется в качестве типа аргументов метода. Это показано в следующем примере, в котором тип R объявлен ковариантным.

    Interface ICovariant(Of Out R)
        Function GetSomething() As R
        ' The following statement generates a compiler error.
        ' Sub SetSomething(ByVal sampleArg As R)
    End Interface
    

    Существует одно исключение из этого правила. Если у вас есть контравариантный универсальный делегат в качестве параметра метода, вы можете использовать этот тип в качестве параметра универсального типа для делегата. Это иллюстрируется типом R в следующем примере. Дополнительные сведения см. в разделах Вариативность в делегатах (Visual Basic) и Использование вариативности для Func и Action Generic Delegates (Visual Basic).

    Interface ICovariant(Of Out R)
        Sub DoSomething(ByVal callback As Action(Of R))
    End Interface
    
  • Тип не используется в качестве универсального ограничения для методов интерфейса. Это показано в следующем коде.

    Interface ICovariant(Of Out R)
        ' The following statement generates a compiler error
        ' because you can use only contravariant or invariant types
        ' in generic constraints.
        ' Sub DoSomething(Of T As R)()
    End Interface
    

Параметр универсального типа можно объявить контравариантным с помощью ключевого in слова. Контравариантный тип можно использовать только в качестве типа аргументов метода, а не в качестве возвращаемого типа методов интерфейса. Контравариантный тип также можно использовать для универсальных ограничений. В следующем коде показано, как объявить контрвариантный интерфейс и использовать универсальное ограничение для одного из его методов.

Interface IContravariant(Of In A)
    Sub SetSomething(ByVal sampleArg As A)
    Sub DoSomething(Of T As A)()
    ' The following statement generates a compiler error.
    ' Function GetSomething() As A
End Interface

Кроме того, можно поддерживать ковариацию и контравариантность в одном интерфейсе, но для различных параметров типа, как показано в следующем примере кода.

Interface IVariant(Of Out R, In A)
    Function GetSomething() As R
    Sub SetSomething(ByVal sampleArg As A)
    Function GetSetSomething(ByVal sampleArg As A) As R
End Interface

В Visual Basic нельзя объявлять события в вариантах интерфейсов без указания типа делегата. Кроме того, вариантный интерфейс не может содержать вложенные классы, перечисления или структуры, но он может иметь вложенные интерфейсы. Это показано в следующем коде.

Interface ICovariant(Of Out R)
    ' The following statement generates a compiler error.
    ' Event SampleEvent()
    ' The following statement specifies the delegate type and
    ' does not generate an error.
    Event AnotherEvent As EventHandler

    ' The following statements generate compiler errors,
    ' because a variant interface cannot have
    ' nested enums, classes, or structures.

    'Enum SampleEnum : test : End Enum
    'Class SampleClass : End Class
    'Structure SampleStructure : Dim value As Integer : End Structure

    ' Variant interfaces can have nested interfaces.
    Interface INested : End Interface
End Interface

реализация универсальных интерфейсов с вариантными параметрами

Вы реализуете вариантные универсальные интерфейсы в классах с помощью того же синтаксиса, который используется для инвариантных интерфейсов. В следующем примере кода показано, как реализовать ковариантный интерфейс в универсальном классе.

Interface ICovariant(Of Out R)
    Function GetSomething() As R
End Interface

Class SampleImplementation(Of R)
    Implements ICovariant(Of R)
    Public Function GetSomething() As R _
    Implements ICovariant(Of R).GetSomething
        ' Some code.
    End Function
End Class

Классы, реализующие вариантные интерфейсы, являются инвариантными. Например, рассмотрим следующий код.

 The interface is covariant.
Dim ibutton As ICovariant(Of Button) =
    New SampleImplementation(Of Button)
Dim iobj As ICovariant(Of Object) = ibutton

' The class is invariant.
Dim button As SampleImplementation(Of Button) =
    New SampleImplementation(Of Button)
' The following statement generates a compiler error
' because classes are invariant.
' Dim obj As SampleImplementation(Of Object) = button

Расширение универсальных интерфейсов с вариативностями

При расширении универсального интерфейса необходимо использовать in ключевые слова и out ключевые слова, чтобы явно указать, поддерживает ли производный интерфейс дисперсию. Компилятор не выводит дисперсию из расширенного интерфейса. Например, рассмотрим следующие интерфейсы.

Interface ICovariant(Of Out T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T)
End Interface

Interface IExtCovariant(Of Out T)
    Inherits ICovariant(Of T)
End Interface

В интерфейсе Invariant(Of T) параметр T универсального типа является инвариантным, в то время как в IExtCovariant (Of Out T)параметре типа является ковариантным, хотя оба интерфейса расширяют один и тот же интерфейс. Это же правило применяется к параметрам контравариантного универсального типа.

Можно создать интерфейс, который расширяет и интерфейс, в котором параметр универсального типа T является ковариантным, и интерфейс, где он является контравариантным, если в интерфейсе расширения параметр универсального типа T является инвариантным. Это показано в следующем примере кода.

Interface ICovariant(Of Out T)
End Interface

Interface IContravariant(Of In T)
End Interface

Interface IInvariant(Of T)
    Inherits ICovariant(Of T), IContravariant(Of T)
End Interface

Однако если параметр T универсального типа объявлен ковариантным в одном интерфейсе, его нельзя объявить контравариантным в интерфейсе расширения или наоборот. Это показано в следующем примере кода.

Interface ICovariant(Of Out T)
End Interface

' The following statements generate a compiler error.
' Interface ICoContraVariant(Of In T)
'     Inherits ICovariant(Of T)
' End Interface

Избегание неоднозначности

При реализации вариантных универсальных интерфейсов вариативность иногда может привести к неоднозначности. Это следует избежать.

Например, если вы явно реализуете один и тот же вариант универсального интерфейса с разными параметрами универсального типа в одном классе, это может создать неоднозначность. Компилятор не создает ошибку в этом случае, но не указывается, какая реализация интерфейса будет выбрана во время выполнения. Это может привести к тонким ошибкам в коде. Рассмотрим следующий пример кода.

Замечание

При использовании Option Strict OffVisual Basic создает предупреждение компилятора при наличии неоднозначной реализации интерфейса. При использовании Option Strict OnVisual Basic создает ошибку компилятора.

' Simple class hierarchy.
Class Animal
End Class

Class Cat
    Inherits Animal
End Class

Class Dog
    Inherits Animal
End Class

' This class introduces ambiguity
' because IEnumerable(Of Out T) is covariant.
Class Pets
    Implements IEnumerable(Of Cat), IEnumerable(Of Dog)

    Public Function GetEnumerator() As IEnumerator(Of Cat) _
        Implements IEnumerable(Of Cat).GetEnumerator
        Console.WriteLine("Cat")
        ' Some code.
    End Function

    Public Function GetEnumerator1() As IEnumerator(Of Dog) _
        Implements IEnumerable(Of Dog).GetEnumerator
        Console.WriteLine("Dog")
        ' Some code.
    End Function

    Public Function GetEnumerator2() As IEnumerator _
        Implements IEnumerable.GetEnumerator
        ' Some code.
    End Function
End Class

Sub Main()
    Dim pets As IEnumerable(Of Animal) = New Pets()
    pets.GetEnumerator()
End Sub

В этом примере не указано, как метод pets.GetEnumerator делает выбор между Cat и Dog. Это может привести к проблемам в коде.

См. также