ジェネリック インターフェイスの分散 (C# および Visual Basic)

.NET Framework 4 では、既存のいくつかのジェネリック インターフェイスに対して、変性のサポートが導入されています。 変性のサポートによって、これらのインターフェイスを実装するクラスの暗黙の型変換が可能になります。 次のインターフェイスがバリアントになりました。

共変性により、メソッドの戻り値の型の派生を、インターフェイスのジェネリック型パラメーターで定義されている型よりも強くすることができます。 共変性の機能について説明するために、ジェネリック インターフェイスである IEnumerable<Object> と IEnumerable<String> (Visual Basic では IEnumerable(Of Object) と IEnumerable(Of String)) について考えます。 IEnumerable<String> (Visual Basic では IEnumerable(Of String)) インターフェイスは、IEnumerable<Object> (Visual Basic では IEnumerable(Of Object)) インターフェイスを継承していません。 ただし、String 型は Object 型を継承しているため、場合によってはこれらのインターフェイスのオブジェクトを相互に割り当てることができます。 次のコード例にこれを示します。

Dim strings As IEnumerable(Of String) = New List(Of String)
Dim objects As IEnumerable(Of Object) = strings
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

旧バージョンの .NET Framework では、C# でも、Visual Basic で Option Strict On を指定した場合でも、このコードはコンパイル エラーの原因となります。 しかし、現在では IEnumerable インターフェイスが共変であるため、前の例に示したように、objects の代わりに strings を使用できます。

反変性により、メソッドの引数の型の派生を、インターフェイスのジェネリック パラメーターで指定されている型よりも弱くすることができます。 反変性について説明するために、BaseClass クラスのインスタンスを比較する BaseComparer クラスを作成したと仮定します。 BaseComparer クラスは、IEqualityComparer<BaseClass> インターフェイス (Visual Basic では IEqualityComparer(Of BaseClass)) を実装します。 現在では、IEqualityComparer インターフェイスが反変であるため、BaseComparer を使用して、BaseClass クラスを継承するクラスのインスタンスを比較できます。 次のコード例にこれを示します。

' Simple hierarchy of classes. 
Class BaseClass
End Class 

Class DerivedClass
    Inherits BaseClass
End Class 

' Comparer class. 
Class BaseComparer
    Implements IEqualityComparer(Of BaseClass)

    Public Function Equals1(ByVal x As BaseClass,
                            ByVal y As BaseClass) As Boolean _
                            Implements IEqualityComparer(Of BaseClass).Equals
        Return (x.Equals(y))
    End Function 

    Public Function GetHashCode1(ByVal obj As BaseClass) As Integer _
        Implements IEqualityComparer(Of BaseClass).GetHashCode
        Return obj.GetHashCode
    End Function 
End Class 
Sub Test()
    Dim baseComparer As IEqualityComparer(Of BaseClass) = New BaseComparer
    ' Implicit conversion of IEqualityComparer(Of BaseClass) to  
    ' IEqualityComparer(Of DerivedClass). 
    Dim childComparer As IEqualityComparer(Of DerivedClass) = baseComparer
End Sub
// Simple hierarchy of classes. 
class BaseClass { }
class DerivedClass : BaseClass { }

// Comparer class. 
class BaseComparer : IEqualityComparer<BaseClass> 
{
    public int GetHashCode(BaseClass baseInstance)
    {
        return baseInstance.GetHashCode();
    }
    public bool Equals(BaseClass x, BaseClass y)
    {
        return x == y;
    }
}
class Program
{
    static void Test()
    {
        IEqualityComparer<BaseClass> baseComparer = new BaseComparer();

        // Implicit conversion of IEqualityComparer<BaseClass> to  
        // IEqualityComparer<DerivedClass>.
        IEqualityComparer<DerivedClass> childComparer = baseComparer;
    }
}

その他の例については、「ジェネリック コレクションに対するインターフェイスでの分散の使用 (C# および Visual Basic)」を参照してください。

ジェネリック インターフェイスの変性がサポートされるのは参照型だけです。 値型は変性をサポートしていません。 たとえば、整数は値型で表されるため、IEnumerable<int> (Visual Basic では IEnumerable(Of Integer)) を IEnumerable<object> (Visual Basic では IEnumerable(Of Object)) に暗黙的に変換することはできません。

Dim integers As IEnumerable(Of Integer) = New List(Of Integer)
' The following statement generates a compiler error 
' with Option Strict On, because Integer is a value type. 
' Dim objects As IEnumerable(Of Object) = integers
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler errror, 
// because int is a value type. 
// IEnumerable<Object> objects = integers;

バリアント インターフェイスを実装するクラスは不変のままである点にも注意する必要があります。 たとえば、List は共変のインターフェイス IEnumerable を実装しますが、List<Object> から List<String> (Visual Basic では List(Of Object) から List(Of String)) に暗黙的に変換することはできません。 これを次のコード例に示します。

' The following statement generates a compiler error 
' because classes are invariant. 
' Dim list As List(Of Object) = New List(Of String) 

' You can use the interface object instead. 
Dim listObjects As IEnumerable(Of Object) = New List(Of String)
// The following line generates a compiler error 
// because classes are invariant. 
// List<Object> list = new List<String>(); 

// You can use the interface object instead.
IEnumerable<Object> listObjects = new List<String>();

参照

関連項目

ジェネリック コレクションに対するインターフェイスでの分散の使用 (C# および Visual Basic)

概念

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

ジェネリック インターフェイス

デリゲートの分散 (C# および Visual Basic)