=============
ОБНОВЛЕНИЕ: я использовал этот ответ в качестве основы для этой записи в блоге:
Почему параметры ref и out не допускают изменения типа?
См. Страницу блога для большего количества комментариев к этой проблеме. Спасибо за отличный вопрос.
=============
Предположим, у вас есть классы Animal
, Mammal
, Reptile
, Giraffe
, Turtle
и Tiger
, с очевидными отношениями подклассов.
Теперь предположим, что у вас есть метод void M(ref Mammal m)
. M
умеет читать и писать m
.
<ч />
Можете ли вы передать переменную типа Animal
в M
?
Нет. Эта переменная может содержать Turtle
, но M
будет предполагать, что она содержит только млекопитающих. A Turtle
не является Mammal
.
Вывод 1 : ref
параметры не могут быть увеличены. (Животных больше, чем млекопитающих, поэтому переменная становится «больше», потому что она может содержать больше вещей.)
<ч />
Можете ли вы передать переменную типа Giraffe
в M
?
Нет. M
может записать в m
, а M
может захотеть записать Tiger
в m
. Теперь вы поместили Tiger
в переменную, которая на самом деле имеет тип Giraffe
.
Вывод 2 : * параметры 1060 * нельзя сделать «меньшими».
<ч />
Теперь рассмотрим N(out Mammal n)
.
Можете ли вы передать переменную типа Giraffe
в N
?
Нет. N
может записать в n
, а N
может захотеть написать Tiger
.
Заключение 3 : * Параметры 1080 * нельзя сделать «меньшими».
<ч />
Можете ли вы передать переменную типа Animal
в N
?
Хм.
Ну, а почему бы и нет? N
не может читать из n
, он может только писать в него, верно? Вы пишете Tiger
в переменную типа Animal
и все готово, верно?
Неправильно. Правило не "N
может писать только в n
".
Правила кратко:
1) N
должен записать в n
, прежде чем N
вернется нормально. (Если N
бросает, все ставки выключены.)
2) N
должен что-то записать в n
, прежде чем он прочитает что-то из n
.
Это разрешает эту последовательность событий:
- Объявите поле
x
типа Animal
.
- Передать
x
в качестве параметра out
на N
.
N
записывает Tiger
в n
, который является псевдонимом для x
.
- В другом потоке кто-то пишет
Turtle
в x
.
N
пытается прочитать содержимое n
и обнаруживает Turtle
в том, что он считает переменной типа Mammal
.
Очевидно, мы хотим сделать это незаконным.
Вывод 4 : out
параметры не могут быть увеличены.
<ч />
Окончательный вывод : Ни параметры ref
, ни out
не могут различаться по своим типам. Чтобы сделать иначе, это нарушить проверяемый тип безопасности.
Если вас интересуют эти вопросы в теории базовых типов, прочитайте мою серию о том, как ковариация и контравариантность работают в C # 4.0 .