Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Parametry typu ogólnego można zadeklarować w interfejsach jako kowariantne lub kontrawariantne. Kowariancja umożliwia metodom interfejsu posiadanie większej liczby pochodnych typów zwracanych niż zdefiniowane przez parametry typu ogólnego. Kontrawariancja umożliwia metodom interfejsu używanie typów argumentów, które są mniej pochodne niż określone przez parametry ogólne. Interfejs ogólny, który ma kowariantne lub kontrawariantne parametry typu ogólnego, jest nazywany wariantem.
Uwaga / Notatka
Program .NET Framework 4 wprowadził obsługę wariancji dla kilku istniejących interfejsów ogólnych. Aby uzyskać listę interfejsów wariantów w programie .NET Framework, zobacz Variance in Generic Interfaces (Visual Basic) (Wariancja w interfejsach ogólnych (Visual Basic).
Deklarowanie wariantów interfejsów ogólnych
Warianty interfejsów ogólnych można zadeklarować przy użyciu in słów kluczowych i out dla parametrów typu ogólnego.
Ważne
ByRef parametrów w języku Visual Basic nie można wariantować. Typy wartości nie obsługują również wariancji.
Za pomocą słowa kluczowego out można zadeklarować kowariantny parametr typu ogólnego. Typ kowariantny musi spełniać następujące warunki:
Typ jest używany tylko jako zwracany typ metod interfejsu i nie jest używany jako typ argumentów metody. Przedstawiono to w poniższym przykładzie, w którym typ
Rjest zadeklarowany kowariantnie.Interface ICovariant(Of Out R) Function GetSomething() As R ' The following statement generates a compiler error. ' Sub SetSomething(ByVal sampleArg As R) End InterfaceIstnieje jeden wyjątek od tej reguły. Jeśli masz delegata ogólnego kontrawariantnego jako parametr metody, możesz użyć tego typu jako parametru typu ogólnego dla delegata. Jest to zilustrowane przez typ
Rw poniższym przykładzie. Aby uzyskać więcej informacji, zobacz Wariancja w delegatach (Visual Basic) i Używanie wariancji dla delegatów ogólnych Func i Action (Visual Basic).Interface ICovariant(Of Out R) Sub DoSomething(ByVal callback As Action(Of R)) End InterfaceTyp nie jest używany jako ograniczenie ogólne dla metod interfejsu. Jest to pokazane w poniższym kodzie.
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
Za pomocą słowa kluczowego in można zadeklarować kontrawariant parametru typu ogólnego. Typ kontrawariantny może być używany tylko jako typ argumentów metody, a nie jako zwracany typ metod interfejsu. Typ kontrawariantny może być również używany w przypadku ograniczeń ogólnych. Poniższy kod pokazuje, jak zadeklarować interfejs kontrawariantny i użyć ogólnego ograniczenia dla jednej z jego metod.
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
Istnieje również możliwość obsługi zarówno kowariancji, jak i kontrawariancji w tym samym interfejsie, ale dla różnych parametrów typu, jak pokazano w poniższym przykładzie kodu.
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
W języku Visual Basic nie można deklarować zdarzeń w interfejsach wariantów bez określania typu delegata. Ponadto interfejs wariantu nie może mieć zagnieżdżonych klas, wyliczeń ani struktur, ale może mieć zagnieżdżone interfejsy. Jest to pokazane w poniższym kodzie.
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
Implementowanie wariantów interfejsów ogólnych
Wariantowe interfejsy ogólne można zaimplementować w klasach za pomocą tej samej składni, która jest używana dla niezmiennych interfejsów. Poniższy przykład kodu pokazuje, jak zaimplementować kowariantny interfejs w klasie ogólnej.
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
Klasy implementujące interfejsy wariantów są niezmienne. Rozważmy na przykład następujący kod.
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
Rozszerzanie wariantów interfejsów ogólnych
Podczas rozszerzania wariantowego interfejsu ogólnego trzeba użyć słów kluczowych in i out aby jawnie określić, czy interfejs pochodny obsługuje wariancję. Kompilator nie wywnioskuje wariancji z interfejsu, który jest rozszerzany. Rozważmy na przykład następujące interfejsy.
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
W interfejsie Invariant(Of T) ogólny parametr T typu jest niezmienny, podczas gdy w IExtCovariant (Of Out T)parametrze typu jest kowariantny, chociaż oba interfejsy rozszerzają ten sam interfejs. Ta sama reguła jest stosowana do kontrawariantnych parametrów typu ogólnego.
Można utworzyć interfejs, który rozszerza zarówno interfejs, gdzie parametr typu ogólnego T jest kowariantny, jak i interfejs, gdzie jest kontrawariantny, pod warunkiem że w interfejsie rozszerzającym parametr typu ogólnego T jest niezmienny. Jest to pokazane w poniższym przykładzie kodu.
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
Jeśli jednak ogólny parametr T typu jest zadeklarowany kowariantnie w jednym interfejsie, nie można zadeklarować jego kontrawariant w interfejsie rozszerzającym lub na odwrót. Jest to pokazane w poniższym przykładzie kodu.
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
Unikanie niejednoznaczności
Podczas implementowania wariantowych interfejsów ogólnych, zmienność może czasami prowadzić do niejednoznaczności. Należy tego unikać.
Jeśli na przykład jawnie zaimplementujesz ten sam wariant interfejs ogólny z różnymi parametrami typu ogólnego w jednej klasie, może to spowodować niejednoznaczność. W tym przypadku kompilator nie generuje błędu, ale nie określono, która implementacja interfejsu zostanie wybrana w czasie wykonywania. Może to prowadzić do drobnych usterek w kodzie. Rozważmy poniższy przykład kodu.
Uwaga / Notatka
W programie Option Strict OffVisual Basic generuje ostrzeżenie kompilatora, gdy istnieje niejednoznaczna implementacja interfejsu. W Option Strict On, Visual Basic generuje błąd kompilatora.
' 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
W tym przykładzie nie określono, w jaki sposób metoda pets.GetEnumerator wybiera pomiędzy Cat i Dog. Może to spowodować problemy w kodzie.