Если вы используете C# 7.0, это довольно легко исправить. Сначала о том, почему это происходит, если неясно. Когда вы выбираете personName
в первый раз, он выбирает адрес для объекта PersonName
, который совпадает с адресом в refObj
. Как только вы вызываете setValueToObject2
, сам объект не изменяется, но новый генерируется по новому адресу. Затем адрес присваивается refObj.PersonName
, но ваша локальная ссылка ничего не знает об этом и все еще указывает на исходный объект, который является правильным и ожидаемым поведением.
Чтобы изменить это и явно следовать изменениям к источнику на refObj.PersonName
вам нужно будет объявить локальную переменную personName
как ref
-Вариабельную. personName
больше не будет указывать на сам объект, но на refObj.PersonName
и все, что находится за этой переменной, поэтому он также будет обновляться при изменении этой переменной.
Рабочий пример будет выглядеть следующим образом:
object refObj = new PersonData();
ReflectionPractice reflection = new ReflectionPractice(refObj);
reflection.setValueToObject1();
ref Name personName = ref (refObj as PersonData).PersonName;
Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);
reflection.setValueToObject2();
Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);
Более подробное объяснение: Когда вы присваиваете объект переменной, вы действительно назначаете ссылку на объект, а не значение самого объекта. Эта ссылка является номером, который говорит нам, где искать данные объекта. Поэтому, если я скажу reflection.setValueToObject1()
, новый объект будет сгенерирован, например, по адресу 1234
. Это число - то, что будет содержать переменная refObj.PersonName
, хотя вы никогда не увидите это число. Когда вы назначаете переменную со ссылкой на этот новый объект, единственное, что нужно сделать, это скопировать это число в новую переменную. Поэтому после того, как вы скажете personName = refObj.PersonName
, personName
теперь также содержит адрес 1234
и, таким образом, указывает на тот же объект, что и refObj.PersonName
.
. Как мы видим, если мы установим refObj.PersonName.Name = "Test"
программу сначала заглянем в refObj.PersonName
и возьмем этот адрес. Затем он переходит на указанный адрес и изменяет значение Name
. То же самое происходит, если вы измените personName.Name
. Сначала он ищет адрес в переменной, переходит на этот адрес и меняет поле Name. Вот почему в этом случае вы увидите изменение имени в обеих переменных, и именно это означает «ссылочный тип» (тип значения создаст копию, поэтому этого не произойдет).
Но; когда вы создаете новый объект, вы также создаете новый адрес для объекта, чтобы жить на - например, 4567
. Вот что делает ключевое слово new
: выделяет немного памяти и создает там новый объект. Этот адрес теперь присваивается refObj.PersonName
, но , а не personName
(поскольку назначение явно происходит только в refObj). Таким образом, personName
все еще имеет адрес 1234
и, следовательно, все еще указывает на старый объект.
Что делает ref
: Когда вы создаете refObj
, он сам (и все поля) будет иметь определенный адрес в памяти. Допустим, refObj
находится на 9900
, а его поле refObj.PersonName
на 9999
. Когда вы говорите personName = refObj.PersonName
, программа смотрит, какой адрес хранится внутри refObj.PersonName
(1234), и копирует его в personName
, но когда вы говорите ref personName = ref refObj.PersonName
, она берет адрес самого поля и копирует его. Так что теперь personName
имеет значение 9999
. Всякий раз, когда вы обращаетесь к personName
сейчас, он сначала просматривает адрес 9999
, а затем следует за тем адресом, который он содержит (1234 или 4567). Вот почему он также будет обновляться при изменении refObj.PersonName
.
Или, если вы больше визуального типа, вот что происходит:
refObj.PersonName = new Name("Name 1")
![refObj.PersonName = new Name(](https://i.stack.imgur.com/Nd2av.png)
personName = refObj.PersonName
![personName = refObj.PersonName](https://i.stack.imgur.com/a6WoR.png)
refObj.PersonName = new Name("Name 2")
![refObj.PersonName = new Name(](https://i.stack.imgur.com/QPBOi.png)
Сравнить что происходит, когда вы используете ключевое слово ref
:
ref personName = ref refObj.PersonName
![ref personName = ref refObj.PersonName](https://i.stack.imgur.com/qjmfC.png)
refObj.PersonName = new Name("Name 2")
![refObj.PersonName = new Name(](https://i.stack.imgur.com/zsxht.png)
Надеюсь, это поможет немного прояснить ситуацию:)