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