Действие <T> или Действие <в T>?

Я читал о Action Delegate на MSDN, и поэтому это под синтаксисом

 public delegate void Action<in T>(T obj);

Чем я просмотрел c-sharpcorner.com и использовал этот синтаксис

public delegate void Action<T>(T obj);   

Как вы видите, нет in до T.
Какой синтаксис правильный и что означает in?
EDIT: тот же синтаксис, используемый для Predicate.

Спасибо.

Ответ 1

Часть in предназначена для ковариации и контравариантности и была представлена ​​в .NET 4.0, статья, на которую вы ссылаетесь, была опубликована в 2006 году до Был выпущен .NET 4.0 (поэтому, очевидно, он не ссылается на дисперсию co [ntra]).

Ответ 2

in и out (общая контравариантность и ковариация) были введены только в С# 4, а делегаты и интерфейсы были изменены для .NET 4 - поэтому Action<T> в .NET 3.5 стал Action<in T> в .NET 4.

Статья, на которую вы ссылаетесь, - с 2006 года, задолго до выхода .NET 4.

Если вы измените версию MSDN, которую вы показываете, вы увидите изменение - например, .NET 3.5 версия показывает он без in.

Ответ 3

Возможно, стоит отметить, что концептуально все типы делегатов теоретически могут быть ковариантными с любым параметром типа, используемым только как возвращаемый тип и контравариантным в отношении параметров типа, используемых только для параметров метода, переданных по значению, а компиляторы могут автоматически разрешать для такой дисперсии, за исключением одной проблемы: в то время как объявление in Action будет препятствовать компилятору кричать, если Action<Animal> передается методу, который ожидает Action<Cat>, некоторые методы, которые ожидают Action<Cat> может вести себя очень плохо, если задано Action<Animal>. В общем, методы, которые должны принимать только делегаты типов с спецификаторами ковариации/контравариантности, если они будут корректно работать со всеми такими делегатами; в противном случае они должны принимать типы делегатов без таких спецификаций.

Большинство методов, которые принимают Action<Cat>, будут отлично работать с Action<Animal>, поэтому Microsoft решила ретроактивно сделать контравариант Action<T>. Поскольку многие методы, принимающие EventHandler<T>, могут сильно сбой, если им дано что-то, что не соответствовало ожидаемому типу, EventHandler<T> не делалось контравариантным.

В ретроспективе, если каждый тип делегата определил свой собственный метод Combine, во всех случаях было бы возможно сделать ковариацию и контравариантность делегатов. Если CatEventArgs:AnimalEventArgs, говоря

EventHandler<CatEventArgs> myEvents=null;
void AddEvent(EventHandler<CatEventArgs> newEvent)
{
  myEvents = EventHandler<CatEventArgs>.Combine(myEvents, newEvent);
}

мог бы превратить переданный EventHandler<AnimalEventsArgs> в EventHandler<CatEventArgs>, который затем можно было бы объединить с любым другим делегатом, который также можно было бы преобразовать в EventHandler<CatEventArgs>. К сожалению, поскольку Combine был определен только на Delegate, нет способа, которым метод Combine может знать, какой тип делегата нужен вызывающему коду [IMHO, даже без ковариации/контравариантности, было бы неплохо есть делегаты, которые определяют свои собственные методы Combine и Remove, так как это могло бы избежать необходимости выводить результат Delegate.Combine].