Udostępnij za pośrednictwem


Tworzenie wariantów interfejsów ogólnych (Visual Basic)

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

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 R jest 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 Interface
    

    Istnieje jeden wyjątek od tej reguły. Jeśli masz kontrawariantnego delegata ogólnego jako parametru metody, możesz użyć typu jako parametru typu ogólnego dla delegata. Jest to zilustrowane przez typ R w poniższym przykładzie. Aby uzyskać więcej informacji, zobacz Variance in Delegates (Visual Basic) (Wariancja w delegatach (Visual Basic) i Using Variance for Func and Action Generic Delegates (Visual Basic) (Używanie wariancji dla elementów Func i Action Generic Delegates (Visual Basic).

    Interface ICovariant(Of Out R)
        Sub DoSomething(ByVal callback As Action(Of R))
    End Interface
    
  • Typ 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, wyliczenia 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

Interfejsy ogólne wariantu można zaimplementować w klasach przy użyciu 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 wariantu interfejsu ogólnego należy użyć in słów kluczowych 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, w którym ogólny parametr T typu jest kowariantny, jak i interfejs, w którym jest kontrawariantny, jeśli w interfejsie rozszerzającym parametr T typu ogólnego 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 interfejsów ogólnych wariantów wariancja 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. Spójrz na poniższy przykład kodu.

Uwaga

W programie Option Strict OffVisual Basic generuje ostrzeżenie kompilatora, gdy istnieje niejednoznaczna implementacja interfejsu. W programie Option Strict OnVisual Basic jest generowany 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 pets.GetEnumerator sposobu wyboru metody między Cat i Dog. Może to spowodować problemy w kodzie.

Zobacz też