建立 Variant 泛型介面(Visual Basic)

您可以將介面中的泛型類型參數宣告為 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之間做選擇。 這可能會在您的程式代碼中造成問題。

另請參閱