Сценарий «Ссылка и тип значения» - PullRequest
4 голосов
/ 17 июля 2010

Я пытался полностью разобраться в типах Reference и Value. Как раз тогда, когда я думал, что у меня есть это, я столкнулся с этим сценарием ...

Я создал класс, который будет содержать один объект.

class Container
{
    public object A {get; set;}
}

Когда я создаю экземпляр этого класса-контейнера (a), я создаю экземпляр ссылочного типа. Я присваиваю целое число объекту в классе. Насколько мне известно, это будет упаковано как объект другого ссылочного типа.

int start = 1;  
Container a = new Container{ A=start };

Я создаю другой экземпляр класса Container (b), но присваиваю ему значение первого контейнера, значение b теперь является ссылкой на a.

Container b = a;

Как и ожидалось, когда я распечатываю значения a.A и b.A, они совпадают.

Console.WriteLine("a.A={0},b.A={1}",a.A,b.A);
//a.A=1,b.A=1

И, как и ожидалось, когда я изменяю значение a.A, значение b.A также изменяется из-за того, что они ссылаются на один и тот же объект.

a.A = 2;

Console.WriteLine("a.A={0},b.A={1}",a.A,b.A);
// a.A=2,b.A=2

Теперь я решил попробовать это, используя отдельные локальные объекты. Я снова помещаю целое число в первый объект и назначаю значение первого объекта второму. Я считаю, что в данный момент объект должен быть ссылочным типом, поэтому c и d должны ссылаться на один и тот же объект. Ничего не меняя, они возвращают одно и то же значение.

int start = 1;
object c = start;
object d = c;

Console.WriteLine("c={0},d={1}",c,d);
// c=1,d=1

Как и раньше, при изменении значения исходного объекта я ожидаю, что значение обоих объектов будет одинаковым.

c = 2;

Console.WriteLine("c={0},d={1}",c,d);
// c=2,d=1

Когда я печатаю результат для этих двух объектов, значение d не меняется, как раньше.

Может кто-нибудь объяснить, почему назначение в этом сценарии отличается от предыдущего?

Спасибо

Ответы [ 4 ]

10 голосов
/ 17 июля 2010

Вот ваша первая ошибка:

Я создаю другой экземпляр класса Container (b), но присваиваю ему значение первого контейнера, значение b теперь является ссылкой на a.

Container b = a;

Это , а не создание другого экземпляра .Он объявляет другую переменную .Теперь обе переменные ссылаются на один и тот же объект.

(я буду редактировать этот ответ, пока буду читать ...)

Далее:

int start = 1;
object c = start;
object d = c;
Console.WriteLine("c={0},d={1}",c,d); // c=1,d=1

Как и раньше, при изменении значения исходного объекта я ожидаю, что значение обоих объектов будет одинаковым.

c = 2;
Console.WriteLine("c={0},d={1}",c,d); // c=2,d=1

Это не меняет объект этоизменение переменной .Каждое из назначений копирует значение - кроме одного из них также выполняется операция упаковки.Давайте немного упростим это:

object c = "first string";
object d = c;

Теперь в этом нет никакого бокса - это просто облегчит его понимание.

Обе переменные в настоящее время имеют значения, относящиеся ктот же объект.Это связано с присваиванием, но больше ничего не связывает две переменные.В данный момент они имеют одинаковое значение, но они являются независимыми переменными.Теперь давайте изменим одно:

c = "different string";

Это изменило значение c для ссылки на другой объект.Если вы распечатаете значения c и d, они будут «другой строкой» и «первой строкой» соответственно. Изменение значения c не меняет значение d.


Теперь давайте вернемся к предыдущему сценарию, чтобы понять, почему он отличается.Там у вас было:

a.A = 2;

Это не меняет значение a.Это изменение данных в объекте, к которому относится a.

Давайте сделаем аналогию с реальным миром, чтобы сделать это проще.Предположим, что все наши переменные - это листы бумаги с домашними адресами, написанными на них.Изменение a.A = 2; похоже на изменение содержимого дома по этому адресу.Конечно, любое другое лицо с тем же адресом, написанным на их листке бумаги, увидит изменение.

Теперь подумайте о вашем c / d сценарии.Опять же, представьте, что у нас есть два листа бумаги, и из-за оператора присваивания у них обоих написан один и тот же адрес.Теперь ваше присвоение нового значения самой переменной c похоже на чистку адреса на листе бумаги c и запись на нем другого адреса.Это совсем не изменит лист бумаги d - на нем все еще будет старый адрес, и если вы посетите этот дом, вы увидите, что ничего не изменилось.Это только то, что написано на листе бумаги, который изменился .

Помогает ли это?

3 голосов
/ 17 июля 2010

Разница заключается в инкапсулирующем объекте Container.

В первом случае у вас есть объект, который содержит ссылку.Когда вы сказали, что создали новый экземпляр класса Container, вы этого не сделали.Вы только что скопировали ссылку на существующий объект.Поскольку у вас есть две ссылки на один и тот же объект, вы можете изменить содержимое объекта с помощью одной ссылки и прочитать его с помощью другой.

 a     b          a     b
  \   /            \   /
   \ /              \ /
---------        ---------
|       |        |       |
|   A   |        |   A   |
|   |   |        |   |   |
----|----   ->   ----|----
    |                |
---------        ---------
|       |        |       |
|   1   |        |   2   |
|       |        |       |
---------        ---------

Во втором случае у вас также есть две ссылки на два одинаковых объекта, но в этом случае вы ссылаетесь на упакованный объект напрямую, а не на контейнер.Когда вы назначаете новое значение одной из ссылок, вы получаете два отдельных объекта.Один объект - в штучной упаковке 1, а другой объект - в штучной упаковке 2. Когда вы назначаете новое значение для b, он не будет помещать значение в поле, на которое он указывает, он создаст новый упакованный объект, содержащий новое значение.

 a     b             a          b
  \   /              |          |
   \ /               |          |
---------   ->   ---------  ---------
|       |        |       |  |       |
|   1   |        |   1   |  |   2   |
|       |        |       |  |       |
---------        ---------  ---------
2 голосов
/ 17 июля 2010

В первом примере у вас есть это:

 a      b  
  \    /  
   \  /  
   |  |  
   v  v  
(Container)

Здесь есть только один экземпляр контейнера.Обе переменные имеют ссылку на этот контейнер.Когда вы изменяете контейнер, обе переменные видят изменение.

Однако в этом коде:

object c = 1;
object d = c;

Второе присваивание не означает, что "d является псевдонимом для c", оно просто означаетчто после второго присваивания c и d указывают на один и тот же объект.

c = 2;

Теперь вы переназначаете c на новое целое в штучной упаковке, так что c и d теперь указывают надва разных объекта.Два объекта никак не связаны между собой.

 Before                 After

 c      d               c                    d
  \    /         c=2    |                    |
   \  /          ---->  |                    |
   |  |                 |                    |
   v  v                 v                    v
(boxed integer 1)      (boxed integer 2)    (boxed integer 1)
0 голосов
/ 17 июля 2010

В вашем первом сценарии у вас была ссылка на один объект в куче. И у вас было две переменные ("a", "b"), ссылающиеся на этот же объект. Вот почему, когда вы изменили элемент этого объекта, вы увидели, что он отражается на обеих переменных.

Во втором случае вы делаете что-то совершенно другое. Когда вы сделали:

c = 2

Вы фактически создали совершенно новый объект. Тип значения int был преобразован в объект, поэтому был создан новый ссылочный объект. В этот момент ваша переменная "d" больше не ссылается на тот же объект, что и ваша переменная "c".

...