Почему «ref» и «out» не поддерживают полиморфизм?

Так как Вы попросили подсказки по книгам... Насколько отладка Windows идет, у John Robbins есть несколько выпусков хорошей книги по отладке Windows:

Приложения Отладки для Microsoft.NET и Примечание Microsoft Windows

, что новый выпуск ( Отладка Приложения Microsoft.NET 2.0 ) является.NET только, таким образом, Вы могли бы хотеть более старый (как в первой ссылке), если Вы хотите отладку собственного кода (это покрывает и.NET и собственный компонент).

123
задан BartoszKP 18 October 2014 в 16:55
поделиться

6 ответов

=============

ОБНОВЛЕНИЕ: Я использовал этот ответ в качестве основы для этой записи в блоге:

Почему параметры ref и out не разрешить изменение типа?

Дополнительные комментарии по этому вопросу см. на странице блога. Спасибо за отличный вопрос.

=============

Предположим, у вас есть классы Animal , Mammal , Reptile , Жираф , Черепаха и Тигр , с очевидными подклассами.

Теперь предположим, что у вас есть метод void M (ref Млекопитающее m) . M может читать и писать m .


Можете ли вы передать переменную типа Animal в M ?

Нет. Эта переменная может содержать Turtle , но M предполагает, что он содержит только млекопитающих. Черепаха не является млекопитающим .

Вывод 1 : ref параметры не могут быть увеличены. (Животных больше, чем млекопитающих, поэтому переменная становится «больше», потому что может содержать больше вещей.)


Можете ли вы передать переменную типа Giraffe в M ?

Нет. M может записывать в m , а M может захотеть записать Tiger в m . Теперь вы поместили Tiger в переменную, которая на самом деле имеет тип Giraffe .

Вывод 2 : ref параметры не могут быть созданы » меньше ».


Теперь рассмотрим N (вне Млекопитающее n) .

Можете ли вы передать переменную типа Giraffe в N ?

Нет. N может писать на n , а N может захотеть написать Tiger .

Вывод 3 : параметры out не могут быть уменьшены.


Можете ли вы передать переменную типа Animal в N ?

Хм.

А почему бы и нет? N не может читать из n , он может только писать в него, верно? Вы записываете Tiger в переменную типа Animal , и все готово, верно?

Неверно. Правило не такое: « N может писать только на n ».

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

1) N должен записать в n , прежде чем N вернется нормально. (Если N выбрасывается, все ставки отменены.)

2) N должен записать что-то в n , прежде чем он прочитает что-то из n .

Это разрешает следующую последовательность событий:

  • Объявить поле x типа Animal .
  • Pass x как Параметр out на N .
  • N записывает Tiger в n , который является псевдонимом для x .
  • В другом потоке кто-то записывает Turtle в x .
  • N пытается прочитать содержимое n , и обнаруживает Turtle в том, что он считает переменной типа Mammal .

Очевидно, мы хотим сделать это незаконным.

Вывод 4 : out параметры не могут быть увеличены.


Окончательный вывод : Ни ref , ни out параметры могут иметь разные типы. Поступить иначе - значит нарушить безопасность проверяемого типа.

Если эти вопросы в базовой теории типов интересуют вас, подумайте о прочтении моей серии статей о том, как ковариация и контравариантность работают в C # 4.0 .

Ни ref , ни out параметры не могут изменять свои типы. Поступить иначе - значит нарушить безопасность проверяемого типа.

Если эти вопросы в базовой теории типов интересуют вас, подумайте о прочтении моей серии статей о том, как ковариация и контравариантность работают в C # 4.0 .

Ни ref , ни out параметры не могут изменять свои типы. Поступить иначе - значит нарушить безопасность проверяемого типа.

Если эти вопросы в базовой теории типов интересуют вас, подумайте о прочтении моей серии статей о том, как ковариация и контравариантность работают в C # 4.0 .

168
ответ дан 24 November 2019 в 01:16
поделиться

Потому что в обоих случаях вы должны иметь возможность присвоить значение параметру ref / out.

Если вы попытаетесь передать b в метод Foo2 как ссылку, а в Foo2 вы попытаетесь присвоить значение = new A (), это будет недопустимым.
По той же причине, по которой вы не можете писать:

B b = new A();
29
ответ дан 24 November 2019 в 01:16
поделиться

Вы боретесь с классической проблемой ООП ковариантности (и контравариантности), см. wikipedia : как бы этот факт ни противоречил интуитивным ожиданиям, математически невозможно разрешить замену производных классов вместо базовых для изменяемых (назначаемых) аргументов (а также контейнеров, элементы которых назначаются по той же причине), при этом соблюдая принцип Лискова . Почему это так, описано в существующих ответах и ​​более подробно исследовано в этих вики-статьях и ссылках на них.

ООП-языки, которые, кажется, делают это, оставаясь при этом традиционно статически безопасными, являются «обманом» (вставка скрытых проверок динамического типа или требование проверки ВСЕХ источников во время компиляции); фундаментальный выбор:

10
ответ дан 24 November 2019 в 01:16
поделиться

Поскольку предоставление Foo2 a ref B приведет к искажению объекта, потому что Foo2 знает только как заполнить A часть B .

2
ответ дан 24 November 2019 в 01:16
поделиться

Разве компилятор не говорит вам, что он хотел бы, чтобы вы явно привели объект, чтобы он мог быть уверен, что вы знаете свои намерения?

Foo2(ref (A)b)
0
ответ дан 24 November 2019 в 01:16
поделиться

Учтите:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

Это нарушит безопасность типов

4
ответ дан 24 November 2019 в 01:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: