Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Платформа .NET Framework 4 представила поддержку дисперсии для нескольких существующих универсальных интерфейсов. Поддержка ковариантности обеспечивает автоматическое преобразование классов, реализующих эти интерфейсы.
Начиная с .NET Framework 4, следующие интерфейсы являются вариантами:
IEnumerable<T> (T является ковариантным)
IEnumerator<T> (T является ковариантным)
IQueryable<T> (T является ковариантным)
IGrouping<TKey,TElement> (
TKey
иTElement
ковариантные)IComparer<T> (T является контравариантным)
IEqualityComparer<T> (T является контравариантным)
IComparable<T> (T является контравариантным)
Начиная с .NET Framework 4.5, следующие интерфейсы являются вариантами:
IReadOnlyList<T> (T является ковариантным)
IReadOnlyCollection<T> (T является ковариантным)
Ковариация позволяет методу иметь более производный тип возвращаемого значения, чем определенный параметром универсального типа интерфейса. Чтобы проиллюстрировать функцию ковариации, рассмотрим следующие универсальные интерфейсы: IEnumerable<Object>
и IEnumerable<String>
. Интерфейс IEnumerable<String>
не наследует IEnumerable<Object>
интерфейс. Однако тип String
наследует Object
тип, и в некоторых случаях может потребоваться назначить объекты этих интерфейсов друг другу. Это показано в следующем примере кода.
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
В более ранних версиях .NET Framework этот код вызывает ошибку компиляции в C# и, если Option Strict
он включен, в Visual Basic. Но теперь можно использовать strings
вместо objects
, как показано в предыдущем примере, потому что интерфейс IEnumerable<T> является ковариантным.
Контравариантность позволяет методу иметь типы аргументов, которые являются менее производными, чем указанные универсальным параметром интерфейса. Чтобы проиллюстрировать контравариантность, предположим, что вы создали BaseComparer
класс для сравнения экземпляров BaseClass
класса. Класс 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>();