=============
UPDATE: я использовал этот ответ в качестве основы для этой записи в блоге:
Почему ref и параметры не позволяют изменять тип?
См. страницу блога для получения дополнительных комментариев по этой проблеме. Спасибо за отличный вопрос.
=============
Предположим, что у вас есть классы Animal
, Mammal
, Reptile
, Giraffe
, Turtle
и Tiger
, с очевидными отношениями подкласса.
Теперь предположим, что у вас есть метод void M(ref Mammal m)
. M
может читать и записывать m
.
Можете ли вы передать переменную типа
blockquote>Animal
наM
?№. Эта переменная может содержать
Turtle
, ноM
предполагает, что она содержит только млекопитающих.Turtle
не являетсяMammal
.Заключение 1: параметры
ref
не могут быть сделаны «большими». (Есть больше животных, чем млекопитающих, поэтому переменная становится «больше», потому что она может содержать больше вещей.)Можете ли вы передать переменную типа
blockquote>Giraffe
наM
?№.
M
может записать наm
, аM
может захотеть записатьTiger
вm
. Теперь вы поместилиTiger
в переменную, которая фактически имеет типGiraffe
.Вывод 2:
ref
параметры не могут быть сделаны «меньше».Теперь рассмотрим
N(out Mammal n)
.Можете ли вы передать переменную типа
blockquote>Giraffe
наN
?№.
N
может писать вn
, аN
может захотеть записатьTiger
.Заключение 3:
out
параметры не могут быть сделаны «меньше».Можете ли вы передать переменную типа
blockquote>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 .