Я 💯 уверен, что вы, возможно, никогда о них не слышали, но вы каждый день используете эти концепции в своем коде.
Ковариация, инвариантность и контравариантность определяют поведение и возможность назначать и использовать производный тип вместо более общего типа.
Математически предположим, что B — базовый тип, а D — производный тип B (B › D)
Для преобразования, примененного f как к B, так и к D,
f ковариантно, когда f(D) можно заменить на f(B)
f контравариантно, когда f( B) можно заменить f(D) [обратным ковариантом]
f инвариантно, когда f(B) и f(D) совершенно разные и нельзя заменить.
Например, предположим, что есть базовый тип Figure и производный тип Square [Square extends Figure]
Пусть эти типы используются для коллекции IEnumerable.
Ковариация, как показано ниже -
// covariance IEnumerable<Square> sq = new List<Square>(); IEnumerable<Figure> f = sq; // No compilation issues - assignable
С другой стороны, это невозможно -
// contravariance IEnumerable<Figure> f = new List<Figure>(); IEnumerable<Square> sq = f; // Compilation error - not assignable // invariance List<Square> f = new List<Square>(); List<Figure> sq = f; // Compilation error - not assignable
Следовательно, вы можете сказать, что IEnumerable и его подтипы ковариантны по параметрам типа, но не контравариантны или инвариантны.
Они упоминаются IEnumerable‹out T›. Другие примеры: IEnumerable‹T›, IEnumerator‹T›, IQueryable‹T› и IGrouping‹TKey,TElement›.
Параметры типа в ковариантах используются только для возвращаемых типов членов.
Некоторые другие универсальные типы имеют параметры контравариантного типа — IComparer‹T›, IComparable‹T› и IEqualityComparer‹T›.
Параметры типа в контравариантах используются только как типы параметров в членах интерфейсов.
Типы действий не ковариантны, а контравариантны по параметрам типа. Вы упоминаете контравариантность как действие‹in T›
// contravariance Action<Figure> b = (f) => { Console.WriteLine(f.GetType().Name); }; Action<Square> d = b; // no compilation error - assignable d(new Square());
Типы Func контравариантны по параметрам типа и ковариантны по возвращаемым типам. Следовательно, вы пишете Func‹in T, out TResult›