Прежде всего, я прочитал много объяснений на SO и в блогах о ковариации и контравариантности, и большое спасибо Эрику Липперту за создание такой замечательной серии о Ковариация и Контравариантность .
Однако у меня есть более конкретный вопрос, который я пытаюсь немного обдумать.
Насколько я понимаю, объяснение Эрика состоит в том, что ковариантность и контравариантность - это прилагательные, которые описывают преобразование. Ковариантное преобразование - это то, что сохраняет порядок типов, а Контравариантное преобразование - это то, что обращает его вспять.
Я понимаю ковариацию таким образом, что я думаю, что большинство разработчиков понимают интуитивно.
//covariant operation
Animal someAnimal = new Giraffe();
//assume returns Mammal, also covariant operation
someAnimal = Mammal.GetSomeMammal();
Операция возврата здесь ковариантна, так как мы сохраняем размер, при котором оба Животных по-прежнему больше, чем Млекопитающие или Жираф. На этом примечании большинство операций возврата являются ковариантными, контрвариантные операции не имеют смысла.
//if return operations were contravariant
//the following would be illegal
//as Mammal would need to be stored in something
//equal to or less derived than Mammal
//which would mean that Animal is now less than or equal than Mammal
//therefore reversing the relationship
Animal someAnimal = Mammal.GetSomeMammal();
Этот фрагмент кода, конечно, не имеет смысла для большинства разработчиков.
Моя путаница заключается в параметрах контравариантного аргумента. Если у вас был метод, такой как
bool Compare(Mammal mammal1, Mammal mammal2);
Я всегда узнал, что входные параметры всегда вызывают противоречивое поведение. Таким образом, если тип используется в качестве входного параметра, его поведение должно быть противоречивым.
Однако в чем разница между следующим кодом
Mammal mammal1 = new Giraffe(); //covariant
Mammal mammal2 = new Dolphin(); //covariant
Compare(mammal1, mammal2); //covariant or contravariant?
//or
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant?
Точно так же, что вы не можете сделать что-то подобное, вы не можете сделать
//not valid
Mammal mammal1 = new Animal();
//not valid
Compare(new Animal(), new Dolphin());
Полагаю, я спрашиваю, что делает аргумент метода передачей контравариантного преобразования.
Извините за длинный пост, возможно, я неправильно это понимаю.
EDIT:
В рамках некоторого разговора, приведенного ниже, я понимаю, что, например, использование уровня делегата может ясно показать противоречивость. Рассмотрим следующий пример
//legal, covariance
Mammal someMammal = new Mammal();
Animal someAnimal = someMammal;
// legal in C# 4.0, covariance (because defined in Interface)
IEnumerable<Mammal> mammalList = Enumerable.Empty<Mammal>();
IEnumerable<Animal> animalList = mammalList;
//because of this, one would assume
//that the following line is legal as well
void ProcessMammal(Mammal someMammal);
Action<Mammal> processMethod = ProcessMammal;
Action<Animal> someAction = processMethod;
Конечно, это незаконно, потому что кто-то может передать любое Животное какому-либо действию, когда ProcessMammal ожидает что-нибудь Млекопитающее или более конкретное (меньше, чем Млекопитающее). Вот почему какое-то действие должно быть только действием или чем-то более конкретным (действием)
Однако это представляет слой делегатов посередине, необходимо ли, чтобы для выполнения противоположного проецирования был посланник посередине? И если бы мы определяли Process как интерфейс, мы объявили бы параметр аргумента как контравариантный тип только потому, что не хотели бы, чтобы кто-то мог делать то, что я показал выше, с делегатами?
public interface IProcess<out T>
{
void Process(T val);
}