Итог: Решарпер изучил ваш тип и обнаружил, что TFrom
может использоваться контравариантно, а TTo
ковариантно. Принятие рефактора позволит вам использовать эти типы с большей гибкостью, как описано ниже. Если это может иметь значение для вас, примите это.
Обратите внимание, однако, что принятие этого рефактора наложит ограничения на использование этих типов в будущем . Если вы когда-нибудь напишите метод, который принимает TTo
в качестве параметра, вы получите ошибку компилятора, поскольку ковиариантные типы не могут быть прочитаны. И то же самое для TFrom
: вы никогда не сможете иметь метод, который возвращает этот тип или имеет параметр out
этого типа.
Это говорит о том, что TFrom
является контравариантным, а TTo
ковариантным. Эти функции были недавно добавлены в C #
Ковариация типа означает, что может быть передан более определенный тип, тогда как контравариантность означает, что менее специфический тип может быть передан.
IEnumerable<T>
является хорошим примером ковариации типов. Поскольку элементы в IEnumerable<T>
являются только для чтения , вы можете установить для него что-то более конкретное:
IEnumerable<object> objects = new List<string>();
Подумайте, что может произойти , если (гипотетически) вам разрешено делать это для коллекций, которые были прочитаны / записаны:
List<object> objects = new List<string>();
objects.Add(new Car());
//runtime exception
Чтобы быть ковариантным по типу, универсальный параметр должен использоваться строго только для чтения ; оно должно быть только когда-либо записано из из типа и никогда не должно читаться в (отсюда и ключевые слова). Вот почему пример IEnumerable<T>
работает, а пример List<T>
- нет. Между прочим, массивы do поддерживают ковариацию типов (так как я полагаю, что Java это делает), и поэтому такая же ошибка времени выполнения возможна с массивами.
Тип контравариантности означает обратное. Для поддержки контравариантности типа универсальный параметр должен читаться только в и никогда не записываться из . Это позволяет заменять менее конкретные типы в.
Action<T>
является примером типа контрава:
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
кодируется для возврата объекта. Если вы установите для него что-то, что возвращает строку, то, опять же, никакого вреда не будет.