Определение контравариантности следующее.Карта F
от типов к типам, отображающая T
в F<T>
, является контравариантной в T
, если всякий раз, когда U
и V
являются типами, такими, что каждый объект типа U
может быть назначен переменнойтип V
, каждому объекту типа F<V>
можно присвоить переменную типа F<U>
(F
отменяет совместимость присваивания).
В частности, если T -> IComparer<T>
, обратите внимание, что переменная типа IComparer<Derived>
может получить объект, реализующий IComparer<Base>
.Это противоречие.
Причина, по которой мы говорим, что IComparer<T>
является контрвариантной в T
, заключается в том, что вы можете сказать
class SomeAnimalComparer : IComparer<Animal> { // details elided }
, а затем:
IComparer<Cat> catComparer = new SomeAnimalComparer();
Редактировать: Выскажи:
Я понимаю, что такое контравариантность и ковариантность.Я спрашиваю, почему эти сравниваемые интерфейсы были изменены по умолчанию на контравариантные.
Изменено?Я имею в виду, IComparer<T>
"естественно" контравариантно.Определение IComparer<T>
:
public interface IComparer<T> {
int Compare(T x, T y);
}
Обратите внимание, что T
появляется только в положении "in" в этом интерфейсе.То есть нет методов, которые возвращают экземпляры T
. Любой такой интерфейс "естественно" контравариантен в T
.
Учитывая это, по какой причине у вас нет желания делать его контравариантным?Если у вас есть объект, который знает, как сравнивать экземпляры U
, а V
совместим по присваиванию с U
, почему бы вам не подумать об этом объекте как о чем-то, что знает, как сравнивать экземплярыV
?Это то, что позволяет контравариантность.
Перед контравариантностью вам нужно было бы завернуть:
class ContravarianceWrapperForIComparer<U, V> : IComparer<V> where V : U {
private readonly IComparer<U> comparer;
public ContravarianceWrapperForIComparer(IComparer<U> comparer) {
this.comparer = comparer;
}
public int Compare(V x, V y) { return this.comparer.Compare(x, y); }
}
И тогда вы могли бы сказать
class SomeUComparer : IComparer<U> { // details elided }
IComparer<U> someUComparer = new SomeUComparer();
IComparer<V> vComparer =
new ContravarianceWrapperForIComparer<U, V>(someUComparer);
Контравариантность позволяет пропустить эти заклинанияи просто скажите
IComparer<V> vComparer = someUComparer;
Конечно, выше было только тогда, когда V : U
.С контравариантностью вы можете делать это, когда U
совместимо с назначением от V
.