Wariancja w interfejsach ogólnych (C#)

Program .NET Framework 4 wprowadził obsługę wariancji dla kilku istniejących interfejsów ogólnych. Obsługa wariancji umożliwia niejawną konwersję klas implementujących te interfejsy.

Począwszy od programu .NET Framework 4, wariantem są następujące interfejsy:

Począwszy od programu .NET Framework 4.5, wariantem są następujące interfejsy:

Kowariancja zezwala metodzie na bardziej pochodny typ zwracany niż zdefiniowany przez ogólny parametr typu interfejsu. Aby zilustrować funkcję kowariancji, rozważ następujące interfejsy ogólne: IEnumerable<Object> i IEnumerable<String>. Interfejs IEnumerable<String> nie dziedziczy interfejsu IEnumerable<Object> . Jednak String typ dziedziczy Object typ, a w niektórych przypadkach można przypisać do siebie obiekty tych interfejsów. Jest to pokazane w poniższym przykładzie kodu.

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

We wcześniejszych wersjach programu .NET Framework ten kod powoduje błąd kompilacji w języku C# i, jeśli Option Strict jest włączony, w Visual Basic. Teraz można jednak użyć wartości strings zamiast objects, jak pokazano w poprzednim przykładzie, ponieważ IEnumerable<T> interfejs jest kowariantny.

Kontrawariancja umożliwia metodzie posiadanie typów argumentów, które są mniej pochodne niż określone przez ogólny parametr interfejsu. Aby zilustrować kontrawariancję, załóżmy, że utworzono klasę BaseComparer do porównywania wystąpień BaseClass klasy. Klasa BaseComparer implementuje interfejs IEqualityComparer<BaseClass>. IEqualityComparer<T> Ponieważ interfejs jest teraz kontrawariantny, można użyć BaseComparer do porównania wystąpień klas, które dziedziczą klasęBaseClass. Jest to pokazane w poniższym przykładzie kodu.

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

Aby uzyskać więcej przykładów, zobacz Używanie wariancji w interfejsach dla kolekcji ogólnych (C#).

Wariancja w interfejsach ogólnych jest obsługiwana tylko w przypadku typów referencyjnych. Typy wartości nie obsługują wariancji. Na przykład IEnumerable<int> nie można niejawnie przekonwertować na IEnumerable<object>wartość , ponieważ liczby całkowite są reprezentowane przez typ wartości.

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

Należy również pamiętać, że klasy implementujące interfejsy wariantów są nadal niezmienne. Na przykład chociaż List<T> implementuje kowariantny interfejs IEnumerable<T>, nie można niejawnie przekonwertować List<String> na List<Object>. Jest to pokazane w poniższym przykładzie kodu.

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

Zobacz też