Вариативность в универсальных интерфейсах (C# и Visual Basic)
В платформе .NET Framework 4 появилась поддержка вариативности для нескольких существующих универсальных интерфейсов. Поддержка вариативности позволяет выполнять неявное преобразование классов, которые реализуют эти интерфейсы. В настоящее время вариативными являются следующие интерфейсы.
IEnumerable (T является ковариантным интерфейсом)
IEnumerator (T является ковариантным интерфейсом)
IQueryable (T является ковариантным интерфейсом)
IGrouping (TKey и TElement являются ковариантными интерфейсами)
IComparer (T является контравариантным интерфейсом)
IEqualityComparer (T является контравариантным интерфейсом)
IComparable (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 является ковариантным.
Контравариация позволяет методу иметь типы аргументов, степень наследования которых меньше, чем указано в параметре универсального типа интерфейса. Чтобы продемонстрировать функцию контравариации, предположим, что создан класс BaseComparer для сравнения экземпляров класса BaseClass. Класс BaseComparer реализует интерфейс IEqualityComparer<BaseClass> (IEqualityComparer(Of BaseClass) в Visual Basic). Поскольку теперь интерфейс IEqualityComparer является контравариантным, можно использовать класс 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 реализует ковариантный интерфейс IEnumerable, нельзя неявно преобразовать 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)