У вас очень много запутанных и ложных убеждений. Давайте исправим это.
Рассмотрим ковариацию, где вы можете заменить производный тип для базового класса.
Это не ковариация . Это совместимость присвоения . Apple совместима с присвоением с переменной типа Fruit, потому что вы можете назначить Apple такой переменной. Опять же, это не ковариация . Ковариантность - это тот факт, что преобразование для типа сохраняет отношение совместимости назначения. последовательность яблок может использоваться где-то, что последовательность фруктов необходима , потому что яблоки - это разновидность фруктов . Это ковариация . Отображение «яблоко -> последовательность яблок, фрукты -> последовательность фруктов» представляет собой ковариантное отображение .
Двигаемся дальше.
Передача typeof(Base)
этому методу и установка этой переменной в производный тип возможны.
Вы путаете типы с экземплярами. Вы не передаете typeof(Base)
этому методу; Вы передаете ссылку на Base
этому экземпляру. typeof(Base)
имеет тип System.Type
.
Как вы правильно заметили, формальные параметры являются переменными . Формальный параметр - это новая переменная, которая инициализируется фактическим параметром 1047 * aka аргумент .
Однако при проверке типа из вызывающей стороны вышеупомянутой функции экземпляр все равно будет иметь тип Base
Правильно. Аргумент имеет тип Base
. Вы копируете это в переменную , а затем переназначаете переменную. Это ничем не отличается от высказывания:
Base x = new Base();
Base y = x;
y = new Derived();
А теперь x
по-прежнему Base
, а y
- Derived
. Вы присвоили одну и ту же переменную дважды; второе назначение выигрывает. Это ничем не отличается от того, что вы сказали бы a = 1; b = a; b = 2;
- вы не ожидаете, что a
будет 2
впоследствии только потому, что вы сказали b = a
в прошлом.
Я бы предположил, что, поскольку объекты по своей природе являются ссылочными, переменная вызывающего теперь будет иметь тип Derived.
Это предположение неверно. Опять же, вы сделали два назначения одной и той же переменной , и у вас есть две переменные , одна в вызывающей и одна в вызываемой. Переменные содержат значения; ссылки на объекты являются значениями .
Единственный способ получить вызывающую функцию типа Derived - это передать опорный объект по ссылке ref, например, так:
Теперь мы подошли к сути проблемы.
Правильный способ думать об этом заключается в том, что ref создает псевдоним переменной . Обычный формальный параметр: , новая переменная . ref
формальный параметр делает переменную в формальном параметре псевдонимом переменной на сайте вызова . Итак, теперь у вас есть одна переменная , но она имеет два имени , потому что имя формального параметра - псевдоним для переменной при вызове. Это так же, как:
Base x = new Base();
ref Base y = ref x; // x and y are now two names for the same variable
y = new Derived(); // this assigns both x and y because there is only one variable, with two names
Более того, я чувствую, что общеизвестно, что передача объекта в метод, редактирование объектов и передача этого объекта в стек сохранит изменения, внесенные в стек. Это имеет смысл, поскольку объект является эталонным объектом.
Правильно.
Ошибка, которую вы делаете здесь, очень распространена. Для команды разработчиков C # было плохой идеей называть функцию псевдонима переменной «ref», потому что это вызывает путаницу. Ссылка на переменную создает псевдоним ; это дает другое имя переменной. Ссылка на объект - это токен, который представляет конкретный объект с определенной идентичностью. Когда вы смешиваете два, это сбивает с толку.
Обычно нужно , а не передавать переменные ref
, особенно если они содержат ссылки.
Что заставляет объект постоянно менять тип в стеке, только если он передается по ссылке?
Теперь у нас самое фундаментальное замешательство. Вы перепутали объекты с переменными . Объект никогда не меняет своего типа, никогда! Яблоко - это объект, а яблоко - это всегда и навсегда яблоко. Яблоко никогда не становится каким-либо другим видом фруктов.
Хватит думать, что переменные - это объекты, прямо сейчас. Ваша жизнь станет намного лучше. Интернализуйте эти правила:
- переменные - это места хранения, в которых хранятся значения
- ссылки на объекты являются значениями
- объекты имеют тип, который никогда не меняется
ref
дает новое имя существующей переменной
- присвоение переменной меняет ее значение
Теперь, если мы зададим ваш вопрос снова, используя правильную терминологию, путаница сразу исчезнет:
Что заставляет значение переменной менять свой тип в стеке, только если оно передано ref
?
Ответ теперь очень ясен:
- Переменная, переданная
ref
, является псевдонимом другой переменной, поэтому изменение значения параметра аналогично изменению значения переменной на сайте вызова
- Присвоение ссылки на объект переменной изменяет значение этой переменной
- Объект имеет определенный тип
Если мы не пройдем мимо ref, а пройдем нормально:
- Передаваемое значение обычно копируется в новую переменную, формальный параметр
- Теперь у нас есть две переменные без связи; изменение одного из них не меняет другого.
Если это все еще неясно, начните рисовать прямоугольники, круги и стрелки на доске, где объекты - это круги, переменные - это поля, а ссылки на объекты - это стрелки от переменных к объектам. Создание псевдонима через ref
дает новое имя существующему кругу; Вызов без ref
делает второй круг и копирует стрелку. Тогда все это будет иметь смысл.