views:

247

answers:

5

I have a List<T> and I do the following:

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

I tried the following but it still updates the item in the List

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

How can I avoid this pointer remaining so I can update properties without it updating it in the list?

+4  A: 

You could make your object implement the ICloneable interface using the MemberwiseClone method and then:

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

UPDATE:

As pointed out in the comments section it is not recommended to implement the ICloneable interface as it does not specify if it will perform a shallow or deep clone. Just add a Clone method to your class.

Darin Dimitrov
The Framework Design Guidelines advise against implementing the ICloneable interface. Just provide a `Clone` method.
dtb
The idea of cloning is the correct one, but do not implement ICloneable. See http://blogs.msdn.com/brada/archive/2003/04/09/49935.aspx for details.
Greg Beech
Is there a quick way to copy all properties to the new object rather than manually typing out all properties explicitly just in case the class gets a new property and the clone method forgets to add that property to copy over.
Jon
See `MemberwiseClone` method.
Darin Dimitrov
+1  A: 

What exactly do you want to do anyway? Do you want to update the property of the object in the list, or do you want another object which is a copy of the object in the list but with a different name?

As an alternative to providing a clone method you could provide a copy constructor.

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

you might be able to do this in a single call, depending on what you want to do. If you know you are always going to create a copy with just a different name then you might want to do something like

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

and set the name during the construction.

but without explicitly creating a new object in some way you can't do what you want.

Be aware though that if you object has a property which is anther object, that you might get into the 'deep copy' problem (which is why IClonable is discouraged) as what do you do when you need to copy that object in you newObj? Do you just provide a reference to the same instance? Or do you copy that as well? What if that object has no clone method/copy constructor, what do you do then?

Sam Holder
+1  A: 

Jon. In C# the items in your array are stored 'by reference', meaning that you do not hold a copy of the object in the array you hold a reference (a 'pointer') to it. When you retrieve the element you are retrieving the reference, so you now have two references to the same object. As you only have one object when you update the value both references 'see' the new value. To avoid this you need to copy the instance and there are a couple of ways to do this. As Darin mentioned you could have the object implement ICloneable you could also and the object implement a copy constructor, or you could create a new object and populate the new one from the 'old' data. However, be aware that there are issues, the primary one being the problem of 'deep copy'. If your object holds references to other objects, how do you copy those? do you copy the reference or do you chase the references down and 'deep copy' those. There is no single answer to this, just the classic 'it depends!'

Kevin Jones
+2  A: 

This has nothing to do with arrays or lists per se - it's just how reference types work. Here's a simpler demonstration:

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

Lists and arrays work the same way - the values stored will be references (assuming they're reference type values) rather than the data for the object itself.

You might want to read my article about reference types and value types for more information.

As others have said, if you really want independent objects you'll need to clone them. I would suggest trying to design around this in another way though, personally.

Jon Skeet
+1  A: 

Great comments, thanks but this is what I have implemented:

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;
    }
Jon