Ответ 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
закодирован для возврата объекта. Если вы установите его на то, что возвращает строку, то, опять же, никакого вреда не было.