ジェネリック インターフェイスの変性 (C#)

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

.NET Framework 4 以降では、次のインターフェイスがバリアントです。

.NET Framework 4.5 以降では、次のインターフェイスがバリアントです。

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

IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

.NET Framework の以前のバージョンでは、C# ではこのコードを実行するとコンパイル エラーが発生し、Visual Basic では Option Strict がオンの場合にエラーが発生します。 今後は、IEnumerable<T> インターフェイスが共変になったので、上記の例のように、objects の代わりに strings を使用できるようになりました。

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

// 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#)」をご覧ください。

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

IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;

また、バリアント インターフェイスを実装するクラスは、現在でもインバリアントであることを忘れないようにしてください。 たとえば、List<T> が共変インターフェイス IEnumerable<T> を実装しても、List<String>List<Object> に暗黙的に変換することはできません。 これを次のコード例に示します。

// 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>();

関連項目