Действие <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
].