Varianz in generischen Schnittstellen (C#)

In .NET Framework 4 wurde die Varianzunterstützung für mehrere vorhandene generische Schnittstellen eingeführt. Die Varianzunterstützung lässt eine implizite Konvertierung von Klassen zu, die diese Schnittstellen implementieren.

Ab .NET Framework 4 sind die folgenden Schnittstellen Varianten:

Ab .NET Framework 4.5 sind die folgenden Schnittstellen Varianten:

Kovarianz ermöglicht einer Methode, stärker abgeleitete Rückgabetypen zu verwenden, als durch die generischen Typparameter der Schnittstelle definiert sind. Betrachten Sie diese generischen Schnittstellen zur Veranschaulichung der Kovarianzfunktionen: IEnumerable<Object> und IEnumerable<String>. Die Schnittstelle IEnumerable<Object> wird nicht von der Schnittstelle IEnumerable<String> geerbt. Allerdings erbt der Typ String den Typ Object. In einigen Fällen können Sie vielleicht auch die Objekte dieser Schnittstellen einander zuweisen. Dies wird im folgenden Codebeispiel gezeigt.

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

In früheren Versionen des.NET-Frameworks verursacht dieser Code in Visual Basic einen Kompilierungsfehler in C# und, wenn Option Strict aktiviert ist. Jetzt können Sie aber strings anstelle von objects verwenden, wie im vorherigen Beispiel gezeigt wurde, da die IEnumerable<T>-Schnittstelle kovariant ist.

Kontravarianz ermöglicht einer Methode, Argumenttypen zu verwenden, die weniger stark abgeleitet sind als durch die generischen Parameter der Schnittstelle angegeben. Nehmen Sie zur Veranschaulichung der Kontravarianz an, dass Sie eine BaseComparer-Klasse zum Vergleich von Instanzen der BaseClass-Klasse erstellt haben. Die BaseComparer-Klasse implementiert die IEqualityComparer<BaseClass>-Schnittstelle. Da die Schnittstelle IEqualityComparer<T> jetzt kontravariant ist, können Sie BaseComparer verwenden, um Instanzen von Klassen zu vergleichen, die die Klasse BaseClass erben. Dies wird im folgenden Codebeispiel gezeigt.

// 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;
    }
}

Weitere Beispiele finden Sie unter Using Variance in Interfaces for Generic Collections (C#) (Verwenden von Varianz in Schnittstellen für generische Auflistungen (C#)).

Varianz in generischen Typparametern wird nur für Referenztypen unterstützt. Werttypen unterstützen keine Varianz. Beispielweise kann IEnumerable<int> nicht implizit in IEnumerable<object> konvertiert werden, da Ganzzahlen durch Werttypen dargestellt werden.

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

Es ist auch wichtig zu beachten, dass Klassen, die variante Schnittstellen implementieren, trotzdem noch invariant sind. Obwohl List<T> beispielsweise die kovariante Schnittstelle IEnumerable<T> implementiert, können Sie List<String> implizit in List<Object> konvertieren. Dies wird im folgenden Codebeispiel veranschaulicht.

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

Siehe auch