Почему я не могу использовать выходные параметры с ковариантными универсальными типами?

Я попробовал следующее, и результат в именах интерфейсов:

interface NotOK<out T>
{
    bool TryDequeue(out T t);
}

interface OK<out T>
{
    T TryDequeue(out bool b);
}

В документация говорит следующее:

Параметры ref и out в C# не могут быть вариантными.

Почему ref не может быть ковариантным (или контравариантным, если на то пошло) очевидно, но почему параметры out не могут быть ковариантными, как и результаты метода?

Является ли это ограничением компилятора или выходные параметры могут фактически нарушить ограничения ковариации?


person Evgeniy Berezovsky    schedule 03.07.2014    source источник
comment
out способ его использования здесь не так сильно отличается от ref (отличие: он используется только как вывод, а не как ввод) - посмотрите ссылку   -  person Random Dev    schedule 03.07.2014
comment
возможный дубликат ref и out параметры в C# и не могут быть помечены как варианты   -  person Evgeniy Berezovsky    schedule 25.09.2014


Ответы (2)


На мой вопрос уже был ответ по адресу параметры ref и out в C# и не могут быть помечены как варианты

Соответствующий фрагмент отличного ответа Эрика Липперта (но это еще не все):

К сожалению нет. «out» на самом деле не отличается от «ref» за кулисами. Единственная разница между "out" и "ref" заключается в том, что компилятор запрещает чтение из параметра out до того, как он будет назначен вызываемым, и что компилятор требует присваивания, прежде чем вызываемый объект вернется в нормальном режиме. Тот, кто написал реализацию этого интерфейса на языке .NET, отличном от C#, сможет прочитать элемент до его инициализации, и поэтому его можно будет использовать в качестве входных данных. Поэтому мы запрещаем помечать T как «out» в этом случае. Это прискорбно, но мы ничего не можем с этим поделать; мы должны соблюдать правила безопасности типов CLR.

person Evgeniy Berezovsky    schedule 24.09.2014

Это связано с тем, что компилятор может подумать, что необходимо небезопасное приведение типов, если вы используете модификатор параметра «out» для ковариации.

Смотрите этот сценарий. Скажем, есть метод f, ожидающий NotOK в качестве входных данных:

interface NotOK<out T>
{
    bool TryDequeue(out T t);
}

void f( NotOK<Animal> x)
{
   bool b ;
   Animal animal_in_f;
   b = x.TryDequeue(animal_in_f);
}

Посмотрите, что произойдет, если у меня есть два интерфейса:

NotOK<Animal> objA;
NotOK<Dog> objD;

Используйте objA в качестве входных данных для f, без проблем.

f(objA);
// objA should have a method of signature bool TryDequeue(out Animal t)
// inside method f, it calls x.TryDequeue(animal_in_f); 
// where animal_in_f is Animal, type match

Но если ковариация разрешена, передача objD будет разрешена

f(objD);
// objD should have a method of signature bool TryDequeue(out Dog t)
// inside method f, it calls x.TryDequeue(animal_in_f); 
// where animal_in_f is Animal
// but this time x.TryDequeue is expecting a Dog!!
// It is unsafe to cast animal_in_f to Dog

Итак, вы понимаете, почему out нельзя использовать в ковариации.

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

Однако я думаю, что разработчик С# взвесил все за и против и, наконец, решил поддерживать последовательное правило проверки типов, которое в целом не допускается.

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

person palazzo train    schedule 19.09.2014