Я 💯 уверен, что вы, возможно, никогда о них не слышали, но вы каждый день используете эти концепции в своем коде.

Ковариация, инвариантность и контравариантность определяют поведение и возможность назначать и использовать производный тип вместо более общего типа.

Математически предположим, что 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›