Подтвердить что ты не робот

Что означает <в TFrom, из TTo> означает?

Resharper предложил изменить с

interface IModelMapper<TFrom, TTo>
{
    TTo Map(TFrom input);
}

в

interface IModelMapper<in TFrom, out TTo>

Итак, я немного расследовал и закончил читать эту статью (найденный в статье в Википедии) и еще несколько Google.

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

Чем яснее, почему я должен его принимать?

4b9b3361

Ответ 1

Нижняя строка: Resharper исследовал ваш тип и обнаружил, что TFrom можно использовать контравариантно, а TTo ковариантно. Принятие рефактория позволит вам использовать эти типы с большей гибкостью, как описано ниже. Если это может быть полезно для вас, примите его.

Обратите внимание, однако, что принятие этого рефактора будет устанавливать ограничения на то, как вы будете использовать эти типы в будущем. Если вы когда-либо пишете метод, который принимает TTo в качестве параметра, вы получите ошибку компилятора, так как типы coviariant не могут быть прочитаны. И так же для TFrom: вы никогда не сможете получить метод, который возвращает этот тип или имеет параметр out этого типа.


Это говорит о том, что TFrom является контравариантным и что TTo является ковариантным. Это были функции недавно добавленные в С#

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

IEnumerable<T> является хорошим примером ковариации типов. Поскольку элементы в IEnumerable<T> являются только для чтения, вы можете установить его в нечто более конкретное:

IEnumerable<object> objects = new List<string>();

Подумайте, что может произойти, если (предположительно) вам было разрешено делать это для коллекций, которые были прочитаны/записаны:

List<object> objects = new List<string>();
objects.Add(new Car());
//runtime exception

Чтобы быть ковариантным, общий параметр должен использоваться строго только для чтения; его нужно только когда-либо написать out из этого типа и никогда не читать в (отсюда и ключевые слова). Вот почему работает пример IEnumerable<T>, но пример List<T> нет. Кстати, массивы поддерживают ковариацию типов (поскольку Java, как я полагаю), и поэтому такая же ошибка времени выполнения возможна с массивами.

Тип контравариантности означает противоположное. Для поддержки контравариантности типов общий параметр должен быть прочитан только в и никогда не записывается out. Это позволяет вам заменять менее конкретные типы.

Action<T> является примером типа contravaince:

Action<object> objAction = (o => Console.WriteLine(o.ToString()));
Action<string> strAction = objAction;
strAction("Hello");

strAction объявлен для принятия строкового параметра, но он отлично работает, если вы подменяете тип объекта. Строка будет передана, но если делегат, с которым он настроен работать, выбирает для обработки как объект, то пусть будет так. Никакого вреда не было.

Для полноты Func<T> - обратный случай Action<T>; здесь T возвращается, поэтому он ковариантен:

Func<string> strDelegate =  () => "Hello";
Func<object> myObjFunc = strDelegate;
object O = myObjFunc();

myObjectFunc закодирован для возврата объекта. Если вы установите его на то, что возвращает строку, то, опять же, никакого вреда не было.

Ответ 2

В качестве примера того, как этот выбор может повлиять на ваше приложение, предположим, что у вас есть тип CustomerAddressMapper, который реализует IModelMapper<Customer, Address[]> и еще один SupplierAddressMapper, который реализует IModelMapper<Supplier, Address[]>. Типы Customer и Supplier совместно используют базовый класс Company, но их логика адресов различна, поэтому нам нужны отдельные типы, чтобы справиться с этим.

Теперь предположим, что у вас есть метод, который принимает IMapper<Company, Address[]>. Перед интерфейсной контравариантностью вы не сможете передавать экземпляры CustomerAddressMapper или SupplierAddressMapper этому методу. Теперь, если вы используете модификатор in в параметре типа TFrom, вы можете.

Кроме того, из-за ковариации по параметру TTo вы также можете передать CustomerAddressMapper методам, которым требуется IMapper<Customer, object>.