您可以將介面中的泛型類型參數宣告為 covariant 或 contravariant。 協變 允許介面方法具有比泛型型別參數所定義更衍生的傳回型別。 Contravariance 可讓介面方法擁有參數類型,其不如泛型參數所指定的類型來得具衍生性。 具有 covariant 或 contravariant 泛型型別參數的泛型介面稱為 variant。
備註
.NET Framework 4 引進了數個現有泛型介面的變異數支援。 如需 .NET Framework 中變體介面的清單,請參閱泛型介面中的變異數(Visual Basic)。
宣告變異泛型介面
您可以使用 泛型型別參數的 in 和 out 關鍵詞來宣告 Variant 泛型介面。
這很重要
ByRef Visual Basic 中的參數不可以是 variant。 實值型別也不支援變異數。
您可以使用 out 關鍵詞來宣告協變的泛型型別參數。 covariant 類型必須滿足下列條件:
此類型只能當做介面方法的傳回型別使用,而不會當做方法自變數的類型使用。 下列範例會說明此範例,其中類型
R宣告為covariant。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 泛型委派中使用變異數(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 關鍵詞來宣告泛型類型參數為逆變(contravariant)。 反變數類型只能當做方法自變數的類型使用,而不是做為介面方法的傳回型別。 反變數類型也可用於泛型條件約束。 下列程式代碼示範如何宣告反變數介面,並對其其中一個方法使用泛型條件約束。
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 中,您無法在不指定委派類型的情況下,在 Variant 介面中宣告事件。 此外,Variant 介面不能有巢狀類別、列舉或結構,但可以有巢狀介面。 下列程式代碼會說明這一點。
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
實作變異型泛型介面
您可以使用用於非變異介面的相同語法,在類別中實作 Variant 泛型介面。 下列程式代碼範例示範如何在泛型類別中實作covariant介面。
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
實作 Variant 介面的類別不可變。 例如,請考慮下列程序代碼。
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
擴充 變體泛型介面
當您擴充 Variant 泛型介面時,必須使用 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
避免模棱兩可
當您實作 Variant 泛型介面時,變異數有時可能會導致模棱兩可。 這應該避免。
例如,如果您在一個類別中明確實作相同變體的泛型介面,但使用了不同的泛型型別參數,這可能會產生模糊。 在此情況下,編譯程式不會產生錯誤,但並未指定將在運行時間選擇哪一個介面實作。 這可能會導致程式代碼中的細微錯誤。 請考慮下列程式碼範例。
備註
使用 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之間做選擇。 這可能會在您的程式代碼中造成問題。