Давайте напишем ⊂
(модифицированный вид символа «меньше») для отношения «есть», которое мы имеем для ссылочных типов.Например, Elephant ⊂ IMammal
, или IMammal ⊂ IAnimal
.
Сначала посмотрите на IObserver<>
:
interface IObserver<in T>
{
void OnNext(T t);
// other members irrelevant
}
«В» означает, что оно противоречиво.Контравариантное означает:
Если X ⊂ Y
, то IObserver<Y> ⊂ IObserver<X>
.
Противоположность заключается в том, что порядок X
и Y
изменяется, когда выприменить IObserver<·>
с обеих сторон ⊂
".Это похоже на умножение неравенства (по математике) на отрицательное число с обеих сторон.Вы должны поменять обе стороны неравенства (или превратить ⊂
в ⊃
).
Причина, по которой разрешено иметь IObserver<T>
контравариант в T
, заключается в том, что T
используется только для параметра-значения в методе (а именно OnNext
).
Предположим, у нас есть экземпляр IObserver<IMammal>
.Это может занять любой IMammal
в его OnNext(IMammal t)
методе.Может ли этот экземпляр действовать как IObserver<Elephant>
?Да, потому что, если он может принимать любой IMmammal
, то, в частности, он может принимать Elephant
, и поэтому контрвариант является целым и невредимым.Поскольку Elephant
"представляет собой" IMammal
, то IObserver<IMammal>
"представляет собой" IObserver<Elephant>
.
Противопоставление меняет соотношение.
Теперь давайте посмотрим на другоетипа (и теперь мы подходим к тому, что вы просите!).Я переименую его с IObservable<>
на IVable<>
:
interface IVable<out T>
{
IDisposable Subscribe(IObserver<T> o);
}
«Out» означает ковариантный.Ковариант означает:
Если X ⊂ Y
, то IVable<X> ⊂ IVable<Y>
.
Это как в математике, когда вы умножаете обе стороны неравенства на положительное номер.X
и Y
являются , а не заменяемыми (совместно).
Но как может IVable<T>
быть ковариантным, когда у него есть метод, Subscribe
, который принимает "что-то с T
"?Это потому, что это «что-то» является контравариантным в T
!
Посмотрим, будет ли оно в целости и сохранности.Итак, предположим, у нас есть экземпляр, который IVable<IMammal>
.Это означает, что у него есть Subscribe
, который может принимать любой IObserver<IMammal>
.Но последнее является контравариантным, поэтому, поскольку IMammal
"является" IAnimal
, то любое IObserver<IAnimal>
"является" IObserver<IMammal>
.Итак, это показывает нам, что наш IVable<IMammal>
может действовать как IVable<IAnimal>
, как указано ковариацией.Так что все хорошо.
В заключение: принятие "во" чего-то, что противоречиво в T
, действует как "вне" в T
.Точно так же, посылая «из» что-то, что является контравариантным в T
, действуйте как «в» в T
.