C # Возвращение объекта из массива оставляет указатель на элемент массива - PullRequest
3 голосов
/ 13 января 2010

У меня есть List<T>, и я делаю следующее:

var myObj = List[2];  //Return object at position 2
myObj.Name = "fred";  //If you look at List[2] its name has changed to fred

Я попробовал следующее, но он все еще обновляет элемент в Списке

var newObj = new MyObj();
var myObj = List[2];  //Return object at position 2
newObj = myObj;
newObj.Name = "fred";  //If you look at List[2] its name has still changed to fred

Как можно избежать сохранения этого указателя, чтобы я мог обновлять свойства без его обновления в списке?

Ответы [ 5 ]

3 голосов
/ 13 января 2010

Вы можете заставить свой объект реализовать интерфейс ICloneable , используя метод MemberwiseClone и затем:

var myObj = List[2];
var newObj = myObj.Clone();
newObj.Name = "fred";

UPDATE:

Как указано в разделе комментариев, не рекомендуется реализовывать интерфейс ICloneable, поскольку в нем не указано, будет ли он выполнять клонирование на мелкой или глубокой основе. Просто добавьте метод Clone в ваш класс.

2 голосов
/ 13 января 2010

Это не имеет ничего общего с массивами или списками как таковыми - это просто то, как работают ссылочные типы. Вот более простая демонстрация:

MyObj a = new MyObj();
MyObj b = a;
a.Name = "Test";
Console.WriteLine(b.Name); // Will print "Test"

Списки и массивы работают одинаково - сохраненные значения будут ссылками (при условии, что они являются ссылочными значениями типа), а не данными для самого объекта.

Возможно, вы захотите прочитать мою статью о ссылочных типах и типах значений для получения дополнительной информации.

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

1 голос
/ 13 января 2010

Замечательные комментарии, спасибо, но это то, что я реализовал:

public MyObj Clone()
    {
        BinaryFormatter bFormatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        bFormatter.Serialize(stream, this);
        stream.Seek(0, SeekOrigin.Begin);
        MyObj newObj = (MyObj)bFormatter.Deserialize(stream);
        return newObj;
    }
1 голос
/ 13 января 2010

Jon. В C # элементы в вашем массиве хранятся «по ссылке», что означает, что вы не храните копию объекта в массиве, где вы храните ссылку («указатель») на него. Когда вы извлекаете элемент, вы извлекаете ссылку, так что теперь у вас есть две ссылки на один и тот же объект. Поскольку у вас есть только один объект при обновлении значения, обе ссылки «видят» новое значение. Чтобы избежать этого, вам нужно скопировать экземпляр, и есть несколько способов сделать это. Как отметил Дарин, вы можете иметь объект, реализующий ICloneable, вы также можете, а объект - конструктор копирования, или вы можете создать новый объект и заполнить новый из «старых» данных. Однако следует помнить, что существуют проблемы, основной из которых является проблема «глубокого копирования». Если ваш объект содержит ссылки на другие объекты, как вы их копируете? копируете ли вы ссылку или вы преследуете ссылки и «копируете» их. На этот вопрос нет однозначного ответа, только классическое «это зависит!»

1 голос
/ 13 января 2010

Что именно ты хочешь делать в любом случае? Хотите обновить свойство объекта в списке или другой объект, который является копией объекта в списке, но с другим именем?

В качестве альтернативы предоставлению метода клонирования вы можете предоставить конструктор копирования.

var newObj = new MyObj(List[2]);
newObj.Name = "fred";

вы можете сделать это за один звонок, в зависимости от того, что вы хотите сделать. Если вы знаете, что вы всегда будете создавать копию с другим именем, тогда вы можете сделать что-то вроде

var newObj = new MyObj(List[2],"fred");

и установите имя во время строительства.

но без явного создания нового объекта вы не сможете делать то, что хотите.

Имейте в виду, что если у вашего объекта есть свойство, являющееся другим объектом, вы можете столкнуться с проблемой «глубокого копирования» (именно поэтому IClonable не рекомендуется), как и то, что вы делаете, когда вам нужно скопировать этот объект ты новенький? Вы просто предоставляете ссылку на тот же экземпляр? Или вы тоже это копируете? Что если у этого объекта нет метода клонирования / конструктора копирования, что вы тогда будете делать?

...