Вариативность в универсальных интерфейсах (C# и Visual Basic)
В платформе .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 является контравариантным интерфейсом)
Ковариация позволяет методу иметь тип возвращаемого значения, степень наследования которого больше, чем указано в параметре универсального типа интерфейса.Чтобы продемонстрировать функцию ковариации, рассмотрим следующие универсальные интерфейсы: IEnumerable<Object> и IEnumerable<String> (IEnumerable(Of Object) и IEnumerable(Of String) в Visual Basic).Интерфейс IEnumerable<String> (IEnumerable(Of String) в Visual Basic) не наследует интерфейс IEnumerable<Object> (IEnumerable(Of Object) в Visual Basic).При этом тип 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.Теперь же можно использовать strings вместо objects, как показано в предыдущем примере, поскольку интерфейс IEnumerable<T> является ковариантным.
Контравариация позволяет методу иметь типы аргументов, степень наследования которых меньше, чем указано в параметре универсального типа интерфейса.Чтобы продемонстрировать функцию контравариации, предположим, что создан класс BaseComparer для сравнения экземпляров класса BaseClass.Класс BaseComparer реализует интерфейс IEqualityComparer<BaseClass> (IEqualityComparer(Of BaseClass) в Visual Basic).Поскольку теперь интерфейс IEqualityComparer<T> является контравариантным, можно использовать класс 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> (IEnumerable(Of Integer) в Visual Basic) нельзя неявно преобразовать в IEnumerable<object> (IEnumerable(Of Object) в Visual Basic), поскольку тип значения — integer.
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<T> реализует ковариантный интерфейс IEnumerable<T>, нельзя неявно преобразовать List<Object> в List<String> (List(Of Object) в List(Of String) в Visual Basic).Это показано в следующем примере кода.
' 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)