Подтвердить что ты не робот

Зачем нужны новые ключевые слова для ковариации и контравариантности в С#?

Может кто-нибудь объяснить, почему необходимо добавить параметр out или in, чтобы указать, что общий тип - вариант Co или Contra в С# 4.0?

Я пытался понять, почему это важно и почему компилятор не может просто понять это.

Спасибо,

Джош

4b9b3361

Ответ 1

Эрик Липперт, который работает над langauge, имеет серию сообщений на msdn, которые должны помочь прояснить затронутые проблемы:
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

При чтении статей, показанных на этой ссылке, начинайте снизу и обрабатывайте.

В конце концов вы перейдете к # 7 (зачем вообще нужен синтаксис?).

Ответ 2

На самом деле мы их не нуждаемся, более того, нам нужны abstract для классов или оба out и ref. Они существуют именно так, что мы, как программисты, можем сделать наше намерение совершенно ясным, чтобы программист-программист знал, что мы делаем, и компилятор может проверить, что мы делаем это правильно.

Ответ 3

Ну, основная проблема заключается в том, что если у вас есть иерархия классов, например:

   class Foo { .. } 

   class Bar : Foo { .. } 

И у вас есть IEnumerator<Bar>, вы не можете использовать это как IEnumerator<Foo>, хотя это будет совершенно безопасно. В 3.5 это вызывает большое количество болезненных колебаний. Эта операция всегда была бы безопасной, но ей отказано в системе типов, потому что она не знает о ковариантном использовании параметра типового типа. IEnumerator<Bar> может возвращать только Bar, а каждый Bar - Foo.

Аналогично, если у вас есть IEqualityComparer<Foo>, он может использоваться для сравнения любой пары объектов типа Foo, даже если один или оба являются Bar, но не могут быть переведены в IEqualityComparer<Bar>, потому что это не знает о контравариантном использовании параметра типового типа. IEqualityComparer<Foo> потребляет только объекты типа Foo, а каждый Bar - Foo.

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

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

Ответ 4

Ключевые слова in и out были ключевыми словами с С# 1.0 и были использованы в контексте параметров in - и out - для методов.

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

Ответ 5

Джон и Джоэл оба предоставили довольно полный ответ на это, но суть в том, что они не столько нужны компилятором, но скорее помогают гарантировать безопасность реализации, явно указывая дисперсия параметра. Это следует очень похожей схеме, требующей ключевое слово out или ref как на вызывающем сайте, так и на сайте объявления.

Ответ 6

Из С# 1.0 массивы, где тип элемента является ссылочным, являются ковариантными. Например, следующий оператор в С# в порядке.

Animal[] animals=new Mammal[10];

В приведенном выше коде млекопитающее может храниться в массиве животных, поскольку млекопитающее происходит от животных. Но помните, что это верно только для ссылочных типов. Почему эта ковариация только для ссылочных типов, но не для типа значений? Это потому, что для ссылочных типов массив первоначально содержит только указатели на исходный объект, а базовый указатель может ссылаться на производные типы. В случае типа значения исходное значение сохраняется в самом массиве, поэтому размер может варьироваться в зависимости от типа. Таким образом, ковариация не поддерживается для массива значений. Например, следующий оператор не будет компилироваться:

long[] arr = new int[100];

Ковариация от метода к делегатам была включена в С# 2.0. В следующих фрагментах кода (которые действительны в С# 2.0 и более поздних версиях) вы обнаружите, что тип возвращаемого значения поддерживает ковариантность. У исходного делегата есть возвращаемый тип Animal. Но метод, который мы назначили (здесь, CopyMammal) для переменной (здесь, cfunc), имеет тип возвращаемого типа Mammal. Таким образом, мы видим, что ковариация поддерживается в обратных типах.

    //delegate which take no arguments but return animal
    delegate Animal copy(); 

/// <span class="code-SummaryComment"><summary> 
</span>

Справедливо следующее утверждение и пример ковариации типа возврата.

//an assignment statement where covariant occurs by allowing 
//Mammal return type in place of Animal return type
copy cfunc = copyMammal;

В параметрах поддерживается контравариантность. Давайте рассмотрим следующие фрагменты кода для понимания того, как контравариантность работает в типах параметров:

//delegate which take one mammal argument and return nothing 
delegate void CopyState(Mammal a); 

void copyMammalState(Mammal mammal) 
{ 
} 

void copyAnimalState(Animal mammal) 
{ 
} 

void CopyGiraffeSate(Giraffe giraffe) 
{  
}

Теперь следующий код будет скомпилирован, поскольку здесь поддерживается корревариантность. Это контравариантность, поскольку мы используем параметр Animal для CopyAnimalState вместо Mammal, определенный в делегате CopyState.

CopyState cs1 = copyAnimalState; 

Но следующий код не будет поддерживаться, поскольку ковариация не поддерживается в параметрах.

CopyState cs2 = CopyGiraffeSate; 

Вышеуказанное недействительно в С#. Но почему это недействительно? Поясним немного. Для аргументации, подумайте, что приведенное выше утверждение действительно. Тогда любой может вызвать cs2 с тигром, как показано ниже:

CopyState cs2 = CopyGiraffeSate;
Tiger tiger = new Tiger();
cs2(tiger);

Если ковариация будет поддерживать здесь, то указанный выше оператор будет генерировать исключение, поскольку cs2 может обрабатывать Жираф, но не Тигр.