インターフェイスでジェネリック型パラメーターを共変または反変として宣言できます。 共分散 により、インターフェイス メソッドはジェネリック型パラメーターで定義された戻り値よりも多くの派生戻り値型を持つことができます。 反変性 により、インターフェイス メソッドは、ジェネリック パラメーターで指定されたよりも派生度の低い引数型を持つことができます。 共変または反変のジェネリック型パラメーターを持つジェネリック インターフェイスは 、バリアント型と呼ばれます。
注
.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
メソッドが Cat
と Dog
を選択する方法は指定されていません。 これにより、コードに問題が発生する可能性があります。
こちらも参照ください
.NET