Этот код теперь компилируется, аргументы типа такие же, как были выше.

ли кто-нибудь предоставить мне простые примеры на C #: конвариантность, контравариантность, инвариантность и противоинвариантность (если такая вещь существует).

Все образцы, которые я видел до сих пор, просто бросали какой-то объект вSystem.Object.

 Pressacco08 апр. 2016 г., 20:36
отклонение не следует путать сКастингони не одно и то же. Видеть:Разница между ковариацией и апскейтингом.
 Dan Tao12 янв. 2011 г., 18:50
Кто-то дал кастинг объектSystem.Object как пример ковариации? Это даже не совсем так.
 Justin Morgan13 янв. 2011 г., 18:26
Я понял, что это означает передачу какого-то объекта (с определенным, более производным типом) вместоSystem.Objectне обязательно кастингobject вSystem.Object, что было бы бессмысленно.

Ответы на вопрос(3)

Решение Вопроса

конвариантность, контравариантность, инвариантность и противоинвариантность (если такая вещь существует).

Я понятия не имею, что означает «противоинвариантность». В остальном все просто.

Вот пример ковариации:

void FeedTheAnimals(IEnumerable<Animal> animals) 
{ 
    foreach(Animal animal in animals)
        animal.Feed();
}
...
List<Giraffe> giraffes = ...;
FeedTheAnimals(giraffes);

IEnumerable<T> интерфейсковариант, Тот факт, что Жираф может быть превращен в животных, подразумевает, чтоIEnumerable<Giraffe> конвертируется вIEnumerable<Animal>, посколькуList<Giraffe> инвентарьIEnumerable<Giraffe> этот код успешно выполняется в C # 4; это не удалось бы в C # 3, потому что ковариация наIEnumerable<T> не работал в C # 3.

Это должно иметь смысл. Последовательность жирафов можно рассматривать как последовательность животных.

Вот пример контравариантности:

void DoSomethingToAFrog(Action<Frog> action, Frog frog)
{
    action(frog);
}
...
Action<Animal> feed = animal=>{animal.Feed();}
DoSomethingToAFrog(feed, new Frog());

Action<T> делегат контравариантен. Тот факт, что лягушка обратима в животных подразумевает, чтоAction<Animal> конвертируется вAction<Frog>, Обратите внимание, как эти отношенияпротивоположное направление ковариантного; вот почему это "против" вариант. Из-за конвертируемости этот код успешно выполняется; это не удалось бы в C # 3.

Это должно иметь смысл. Действие может принять любое животное; нам нужно действие, которое может предпринять любая Лягушка, и действие, которое может предпринять любое Животное, безусловно, может также взять любую Лягушку.

Пример инвариантности:

void ReadAndWrite(IList<Mammal> mammals)
{
    Mammal mammal = mammals[0];
    mammals[0] = new Tiger();
}

Можем ли мы передатьIList<Giraffe> к этой вещи? Нет, потому что кто-то собирается вписать в него Тигра, а Тигра не может быть в списке Жирафов. Можем ли мы передатьIList<Animal> в эту вещь? Нет, потому что мы собираемся прочитать Млекопитающее из него, и список Животных мог бы содержать Лягушку.IList<T> являетсяинвариантный, Его можно использовать только таким, какой он есть на самом деле.

Дополнительные соображения по поводу дизайна этой функции см. В моей серии статей о том, как мы ее спроектировали и создали.

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

 Eric Lippert12 янв. 2011 г., 19:15
@Dan: альтернативно, фактически неизменяемый тип массива, который гарантированно не будет доступен для записи после его инициализации, будет безопасно ковариантным. Я хотел бы иметь такую ​​вещь. Тот факт, что массивы являются коллекциейпеременные часто раздражает; Мне нужна коллекцияценности.
 Dan Tao12 янв. 2011 г., 18:59
@Eric: Да, я определенно понимаю причину (я думаю): часто разработчики хотят «подмножество» интерфейсаT[] (или жеIList<T>), чтобы получить доступ к элементам по индексу без намерения записи в коллекцию. Вот почему я лично твердо верю, что должно быть что-то вроде ковариантаIArray<T> интерфейс с индексированным геттером в BCL. Это позволило быList<Giraffe> вести себя какIList<Mammal> когда он используется только для индексированного доступа, а не его изменяемых характеристик (т.е. в том же случае, когдаGiraffe[] может безопасно действовать какMammal[]).
 Eric Lippert12 янв. 2011 г., 18:51
@ Дан: Хорошая мысль. C # поддерживает ковариацию «битых» массивов, где допускаются некоторые ковариантные преобразования, даже если они могут вызывать сбои во время выполнения.
 Anthony Pegram12 янв. 2011 г., 19:19
Является ли массив ковариации почемуnew ReadOnlyCollection<Animal>(new Frog[] { new Frog() }); компилирует (и работает) ноnew ReadOnlyCollection<Animal>(new List<Frog> { new Frog() }); не? (В этом случае параметр конструктораIList<Animal>).
 Dan Tao12 янв. 2011 г., 18:44
Конечно, в последнем примере стоит указать, что вымог броситьGiraffe[] кMammal[] и передать это, что приведет к ошибке во время выполнения.

вы регулярно используете интерфейс для обработки объекта, но объект является экземпляром класса - вы не можете создать экземпляр интерфейса. Используйте простой список строк в качестве примера.

IList<string> strlist = new List<string>();

Я уверен, что вы знаете о преимуществах использованияIList<> а не напрямую используяList<>, Это позволяет инвертировать управление, и вы можете решить, что не хотите использоватьList<> больше, но вы хотитеLinkedList<> вместо. Приведенный выше код работает нормально, потому что общий тип интерфейса и класса одинаков:string.

Это может быть немного сложнее, если вы хотите сделать список списка строк, хотя. Рассмотрим этот пример:

IList<IList<string>> strlists = new List<List<string>>();

Это явно не скомпилируется, потому что аргументы универсальных типовIList<string> а такжеList<string> не то же самое. Даже если вы объявили внешний список как обычный класс, какList<IList<string>>, он не будет компилироваться - аргументы типа не совпадают.

Так вот, где ковариация может помочь. Ковариантность позволяет использоватьболее производный тип в качестве аргумента типа в этом выражении. ЕслиIList<> было сделано, чтобы быть ковариантным, он просто компилирует и решает проблему. К несчастью,IList<> не является ковариантным, но один из расширяемых интерфейсов:

IEnumerable<IList<string>> strlists = new List<List<string>>();

Этот код теперь компилируется, аргументы типа такие же, как были выше.

так и противо-дисперсии. Итак, словопротивопоказаны инвариантность не имеет смысла. Любой параметр типа, который не помечен какin или жеout инвариантен Это означает, что этот параметр типа может использоваться и возвращаться.

Хороший пример совместной дисперсииIEnumerable<out T> потому чтоIEnumerable<Derived> можно заменить наIEnumerable<Base>, Или жеFunc<out T> который возвращает значения типаT.
Например,IEnumerable<Dog> может быть преобразован вIEnumerable<Animal> потому что любая собака это животное.

Для противоречий вы можете использовать любой потребляющий интерфейс или делегат.IComparer<in T> или жеAction<in T> пришло мне в голову. Они никогда не возвращают переменную типаTТолько получи. Везде, где вы ожидаете получитьBase Вы можете пройти вDerived.

Думая о них как о входных или только выходных типовых параметрах, легче понять IMO.

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

 Behnam Esmaili17 апр. 2018 г., 13:53
спасибо за "быть поглощенным и возвращенным."

Ваш ответ на вопрос