1

I have a very simple question that is taking me a long time to figure out... How do I copy an object of an item and if that object changes, the instance doesn't?

For example, in my code I have this:

nextMove[i] = nextCell;
listOfNextMoves.Add(new State(nextMove));
nextMove[i] = Cell.Empty;

the nextMove that I have added to my listOfNextMoves changes to what the third line did.... How do I make it so if nextMove[i] changes, the nextMove inside of the list does not change?

In python, if I wanted to do

tempBoard = genBoard

I would have to do this

tempBoard = list(genBoard)

I am sorry if I am not clear, I have been stuck on this for a while.

user3369494
  • 123
  • 11

3 Answers3

3

The ToList() function creates a copy of a list:

nextMoveCopy = nextMove.ToList();

You can also use the constructor for most collection classes:

nextMoveCopy = new ArrayList(nextMove); 
nextMoveCopy = new List< GameBoard >(nextMove); 
nextMoveCopy = new ObservableCollection< GameBoard >(nextMove); 

Do NOT use ReadOnlyCollection for this. Instead of making a copy, it creates a read-only wrapper around the original list.

Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
1

In general, objects in C# cannot be copied.

However, many specific objects provide ways to duplicate themselves. Usually this is indicated by implementing the ICloneable interface. For example, string implements ICloneable so you can create a new string with the same value as an existing string by calling String.Clone and casting the result from an object to a string. Since strings are immutable, this isn't very useful. You never need to clone a string to protect it's value from changing, because it can't be changed. For other objects, though, it's more useful.

Arrays can also be cloned. For example:

var myArray = new MyType[3];
myArray[1] = new MyType("some parameter");
myArray[2] = new MyType("some other parameter");
var myOtherArray = (myArray[]) myArray.Clone();
myOtherArray[2].Parameter // value is "some other parameter"
myOtherArray[2] = new MyType("a whole other parameter");
myOtherArray[2].Parameter // value is "a whole other parameter"
myArray[2].Parameter // value is still "some other parameter"

Because arrays contain references to objects, cloning the array won't clone all the objects in the array.

myArray[1].Parameter = "changed parameter";
myOtherArray[1].Parameter // value is now "changed parameter"

Here there are two different arrays, with two different objects in the third spot, but the same object in the second spot.

Generally, Clone is "messy". Many complicated objects are not cloneable, and just because an type implements ICloneable doesn't mean it works as expected. Clone usually creates what's called a shallow copy, which means that the object itself is cloned, but other objects it references are not cloned. Sometimes Clone will create a deep copy, where all references the object has are cloned too. Sometimes it will mix them. Sometimes developers inherit from types which implement ICloneable but don't provide a new implementation for their subtype. Sometimes they forget to make their implementations virtual. All the problems with ICloneable are outside the scope of this answer but, as a rule, avoid Clone if at all possible.

Instead, use copy constructors if available. Many objects can be created using a reference to other objects of the same type as a template. For example

var myList = new List<string>(){"a","b"};
var myOtherList = new List<string>(myList); // a copy of myList!

You can also use the linq extensions to turn arrays and lists into copied arrays and lists.

using System.Linq;
// ...
var myArray = new[]{"a","b"};
var myOtherArray = myArray.ToArray(); // creates a new array
var myList = myArray.ToList(); // creates a new list

In your example, try

using System.Linq;
// ...
nextMove[i] = nextCell;
listOfNextMoves.Add(new State(nextMove.ToArray()));
nextMove[i] = Cell.Empty;
0

I think I figured it out!

I just had to do

nextMove[i] = nextCell;
listOfNextMoves.Add(new State((Cells[])nextMove.Clone()));
nextMove[i] = Cell.Empty;

and it created a deep copy of the instance for me!

user3369494
  • 123
  • 11
  • Actually depending on how the `Clone()` method was implemented it could be either a shallow copy or a deep copy (most likely a shallow copy though). Your code works because it doesn't matter if the copy is shallow or deep. I would go with Jonathan Allen's solution though since the intent is more obvious. – Setsu Feb 27 '15 at 01:06