共用方式為


建立 Variant 泛型介面(Visual Basic)

您可以將介面中的泛型類型參數宣告為 covariant 或 contravariant。 協變 允許介面方法具有比泛型型別參數所定義更衍生的傳回型別。 Contravariance 可讓介面方法擁有參數類型,其不如泛型參數所指定的類型來得具衍生性。 具有 covariant 或 contravariant 泛型型別參數的泛型介面稱為 variant

備註

.NET Framework 4 引進了數個現有泛型介面的變異數支援。 如需 .NET Framework 中變體介面的清單,請參閱泛型介面中的變異數(Visual Basic)。

宣告變異泛型介面

您可以使用 泛型型別參數的 inout 關鍵詞來宣告 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 泛型介面時,必須使用 inout 關鍵詞來明確指定衍生介面是否支援變異數。 編譯程式不會從正在擴充的介面推斷變異數。 例如,請考慮下列介面。

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方法如何在CatDog之間做選擇。 這可能會在您的程式代碼中造成問題。

另請參閱