2010-07-17 2 views
4

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

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

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

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

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

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

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 не изменяется, как раньше.

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

Благодаря

ответ

10

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

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

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 - у нее все еще есть старый адрес, и если вы посетите этот дом, вы увидите, что ничего не изменилось. Это только то, что написано на листе бумаги, который был изменен.

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

2

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

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

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

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

c = 2 

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

3

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

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

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

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

a  b    a   b 
    \ /   |   | 
    \/    |   | 
--------- -> --------- --------- 
|  |  |  | |  | 
| 1 |  | 1 | | 2 | 
|  |  |  | |  | 
---------  --------- --------- 
Смежные вопросы