Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Параметры универсального типа можно объявить в интерфейсах как ковариантный или контравариантный. Ковариация позволяет методам интерфейса иметь более производные типы возвращаемых значений, чем определенные параметрами универсального типа. Контравариантность позволяет методам интерфейса иметь типы аргументов, которые являются менее производными, чем указанные универсальными параметрами. Универсальный интерфейс с ковариантными или контравариантными параметрами универсального типа называется вариантом.
Замечание
Платформа .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 Off
Visual Basic создает предупреждение компилятора при наличии неоднозначной реализации интерфейса. При использовании Option Strict On
Visual 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
. Это может привести к проблемам в коде.