Dela via


Skapa generiska variantgränssnitt (Visual Basic)

Du kan deklarera generiska typparametrar i gränssnitt som covarianta eller kontravarianta. Med kovarians kan gränssnittsmetoder ha fler härledda returtyper än de som definieras av parametrar av allmän typ. Med kontravarians kan gränssnittsmetoder ha argumenttyper som är mindre härledda än de som anges av de allmänna parametrarna. Ett allmänt gränssnitt som har parametrar av typen covariant eller kontravariant generisk typ kallas variant.

Kommentar

.NET Framework 4 introducerade variansstöd för flera befintliga generiska gränssnitt. En lista över variantgränssnitten i .NET Framework finns i Varians i Generiska gränssnitt (Visual Basic).

Deklarera generiska gränssnitt för variant

Du kan deklarera generiska variantgränssnitt med hjälp av nyckelorden in och out för generiska typparametrar.

Viktigt!

ByRef parametrar i Visual Basic kan inte vara variant. Värdetyper stöder inte heller varians.

Du kan deklarera en allmän typparameter covariant med hjälp av nyckelordet out . Den varianta typen måste uppfylla följande villkor:

  • Typen används endast som en returtyp av gränssnittsmetoder och används inte som en typ av metodargument. Detta illustreras i följande exempel, där typen R deklareras som variant.

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

    Det finns ett undantag till den här regeln. Om du har ett kontravariant generiskt ombud som en metodparameter kan du använda typen som en allmän typparameter för ombudet. Detta illustreras av typen R i följande exempel. Mer information finns i Varians i Ombud (Visual Basic) och Using Variance for Func and Action Generic Delegates (Visual Basic).

    Interface ICovariant(Of Out R)
        Sub DoSomething(ByVal callback As Action(Of R))
    End Interface
    
  • Typen används inte som en allmän begränsning för gränssnittsmetoderna. Detta illustreras i följande kod.

    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
    

Du kan deklarera en generisk typparameter kontravariant med hjälp av nyckelordet in . Den kontravarianta typen kan endast användas som en typ av metodargument och inte som en returtyp av gränssnittsmetoder. Typen contravariant kan också användas för allmänna begränsningar. Följande kod visar hur du deklarerar ett kontravariant gränssnitt och använder en allmän begränsning för någon av dess metoder.

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

Det är också möjligt att stödja både kovarians och kontravarians i samma gränssnitt, men för olika typparametrar, som visas i följande kodexempel.

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

I Visual Basic kan du inte deklarera händelser i variantgränssnitt utan att ange ombudstypen. Dessutom kan ett variantgränssnitt inte ha kapslade klasser, uppräkningar eller strukturer, men det kan ha kapslade gränssnitt. Detta illustreras i följande kod.

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

Implementera generiska variantgränssnitt

Du implementerar generiska variantgränssnitt i klasser med samma syntax som används för invarianta gränssnitt. Följande kodexempel visar hur du implementerar ett covariant-gränssnitt i en allmän klass.

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

Klasser som implementerar variantgränssnitt är invarianta. Tänk till exempel på följande 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

Utökar generiska variantgränssnitt

När du utökar ett generiskt variantgränssnitt måste du använda nyckelorden in och out för att uttryckligen ange om det härledda gränssnittet stöder varians. Kompilatorn härleder inte variansen från gränssnittet som utökas. Tänk till exempel på följande gränssnitt.

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) I gränssnittet är den generiska typparametern T invariant, medan i IExtCovariant (Of Out T)typparametern är covariant, även om båda gränssnitten utökar samma gränssnitt. Samma regel tillämpas på parametrar av typen contravariant generic type.

Du kan skapa ett gränssnitt som utökar både gränssnittet där den generiska typparametern T är covariant och gränssnittet där det är kontravariant om den generiska typparametern T i utökande gränssnitt är invariant. Detta illustreras i följande kodexempel.

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

Men om en allmän typparameter T deklareras som covariant i ett gränssnitt kan du inte deklarera den som kontravariant i det utökade gränssnittet eller vice versa. Detta illustreras i följande kodexempel.

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

Undvika tvetydighet

När du implementerar generiska variantgränssnitt kan varians ibland leda till tvetydighet. Detta bör undvikas.

Om du till exempel uttryckligen implementerar samma generiska variantgränssnitt med olika generiska typparametrar i en klass kan det skapa tvetydighet. Kompilatorn skapar inte något fel i det här fallet, men det anges inte vilken gränssnittsimplementering som ska väljas vid körning. Detta kan leda till subtila buggar i koden. Ta följande kod som exempel.

Kommentar

Med Option Strict Offgenererar Visual Basic en kompilatorvarning när det finns en tvetydig gränssnittsimplementering. Med Option Strict Ongenererar Visual Basic ett kompilatorfel.

' 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

I det här exemplet är det ospecificerat hur pets.GetEnumerator metoden väljer mellan Cat och Dog. Detta kan orsaka problem i koden.

Se även