次の方法で共有


バリアント ジェネリック インターフェイスの作成 (Visual Basic)

インターフェイスでジェネリック型パラメーターを共変または反変として宣言できます。 共分散 により、インターフェイス メソッドはジェネリック型パラメーターで定義された戻り値よりも多くの派生戻り値型を持つことができます。 反変性 により、インターフェイス メソッドは、ジェネリック パラメーターで指定されたよりも派生度の低い引数型を持つことができます。 共変または反変のジェネリック型パラメーターを持つジェネリック インターフェイスは 、バリアント型と呼ばれます。

.NET Framework 4 では、複数の既存のジェネリック インターフェイスに差異のサポートが導入されました。 .NET Framework のバリアント インターフェイスの一覧については、「 ジェネリック インターフェイスの分散 (Visual Basic)」を参照してください。

バリアント ジェネリック インターフェイスの宣言

ジェネリック型パラメーターの in キーワードと out キーワードを使用して、バリアント ジェネリック インターフェイスを宣言できます。

Von Bedeutung

ByRef Visual Basic のパラメーターをバリアントにすることはできません。 値型も分散をサポートしていません。

out キーワードを使用して、共変のジェネリック型パラメーターを宣言できます。 共変型は、次の条件を満たす必要があります。

  • 型はインターフェイス メソッドの戻り値の型としてのみ使用され、メソッド引数の型として使用されません。 次の例では、 R 型が共変として宣言されています。

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

    この規則には 1 つの例外があります。 メソッド パラメーターとして反変ジェネリック デリゲートがある場合は、デリゲートのジェネリック型パラメーターとして型を使用できます。 これは、次の例で R 型によって示されています。 詳細については、「 デリゲートの分散 (Visual Basic)」 および「 Func および Action Generic Delegate の分散の使用 (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 キーワードを使用して、ジェネリック型パラメーター反変を宣言できます。 反変型は、メソッド引数の型としてのみ使用でき、インターフェイス メソッドの戻り値の型として使用することはできません。 反変型は、ジェネリック制約にも使用できます。 次のコードは、反変インターフェイスを宣言し、そのメソッドの 1 つにジェネリック制約を使用する方法を示しています。

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 では、デリゲート型を指定しないと、バリアント インターフェイスでイベントを宣言することはできません。 また、バリアント インターフェイスには、入れ子になったクラス、列挙型、または構造体を含めることはできませんが、入れ子になったインターフェイスを持つことができます。 これを次のコードに示します。

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

バリアント ジェネリック インターフェイスの実装

バリアント ジェネリック インターフェイスは、インバリアント インターフェイスに使用されるのと同じ構文を使用してクラスに実装します。 次のコード例は、ジェネリック クラスで共変インターフェイスを実装する方法を示しています。

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

バリアント インターフェイスを実装するクラスは不変です。 たとえば、次のコードを考えてみましょう。

 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

バリアント ジェネリック インターフェイスの拡張

バリアント ジェネリック インターフェイスを拡張する場合は、 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 が 1 つのインターフェイスで共変として宣言されている場合、拡張インターフェイスで反変を宣言することはできません。またはその逆も同様です。 これを次のコード例に示します。

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

あいまいさを回避する

バリアント ジェネリック インターフェイスを実装すると、分散があいまいになることがあります。 これは避ける必要があります。

たとえば、異なるジェネリック型パラメーターを持つ同じバリアント ジェネリック インターフェイスを 1 つのクラスに明示的に実装すると、あいまいさが生まれる可能性があります。 この場合、コンパイラはエラーを生成しませんが、実行時にどのインターフェイス実装を選択するかは指定されていません。 これにより、コードに微妙なバグが発生する可能性があります。 次のコード例について考えます。

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を選択する方法は指定されていません。 これにより、コードに問題が発生する可能性があります。

こちらも参照ください