Может ли кто-нибудь объяснить использование части T с примером?
Конечно.IEnumerable<T>
является ковариантным.Это означает, что вы можете сделать это:
static void FeedAll(IEnumerable<Animal> animals)
{
foreach(Animal animal in animals) animal.Feed();
}
...
IEnumerable<Giraffe> giraffes = GetABunchOfGiraffes();
FeedAll(giraffes);
«Ковариант» означает, что отношение совместимости присваивания аргумента типа сохраняется в универсальном типе .Giraffe
совместимо с присвоением с Animal
, и поэтому это отношение сохраняется в составных типах: IEnumerable<Giraffe>
совместимо с присвоением с IEnumerable<Animal>
.
Почему применимо только для интерфейсов и делегатова не для классов?
Проблема с классами состоит в том, что классы имеют тенденцию иметь изменяемые поля.Давайте возьмем пример.Предположим, мы допустили следующее:
class C<out T>
{
private T t;
Хорошо, теперь тщательно продумайте этот вопрос, прежде чем продолжить. Может ли C<T>
иметь какой-либо метод вне конструктора, который устанавливает для поля t
значение, отличное от значения по умолчанию?
Поскольку оно должно быть безопасным, C<T>
теперь может не иметьметоды, которые принимают T в качестве аргумента;Т можно только вернуть.Так, кто устанавливает t, и , откуда они получают значение, которое они устанавливают из ?
Типы ковариантных классов действительно работают, только если класс неизменен .И у нас нет хорошего способа сделать неизменяемые классы в C #.
Хотелось бы, чтобы мы это делали, но мы должны жить с системой типов CLR, которую нам дали.Я надеюсь, что в будущем мы сможем получить лучшую поддержку как для неизменных классов, так и для ковариантных классов.
Если эта функция вас интересует, подумайте о прочтении моих длинных серий о том, как мы разработали и реализовали эту функцию.Начните снизу:
https://blogs.msdn.microsoft.com/ericlippert/tag/covariance-and-contravariance/