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 Off
genererar Visual Basic en kompilatorvarning när det finns en tvetydig gränssnittsimplementering. Med Option Strict On
genererar 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.