0

When using foreach on a List collection, the iterated objects are obviously changed in the collection. I wish to keep a collection "copy" for later use, but this "copy" seems to be changed along with the original list collection.

How can i keep my "copy" collection from being changed when looping through my original list collection?

This is what I've tried so far:

private List<T> _listCopy;
public Constructor(List<T> inputList)
{
    _listCopy = new List<T>(inputList);

    foreach(var obj in inputList) {
    // This changes both inputList and _listCopy.
    // How can i keep _listCopy, from being edited as well?
    }
}
Johnson
  • 55
  • 9
  • What makes you think objects are changed when iterating over collection? – dotnetom Dec 14 '14 at 16:40
  • I mean when i edit an object in the collection through foreach. Then the object is also edited in my "copy" collection. – Johnson Dec 14 '14 at 16:41
  • 3
    So it's not the list that is updated, it is the object that is updated. You should read about what references are in C# and how they work and you will understand why the objects in both collections are updated – dotnetom Dec 14 '14 at 16:42
  • I'll start reading. Thank you. – Johnson Dec 14 '14 at 16:55
  • As it turns out this question about something else that what you actaully want to know... – t3chb0t Dec 14 '14 at 17:07

2 Answers2

2

If your type T is a reference type then the array copy is a copy of the list structure, but not a copy of the list items! The copied list still contains references to the original items! If you want to make changes to the objects in the list and keep the original objects intact, you must clone the objects as well!

You can add a clone method to a class like this, using this interface definition:

public interface IShallowClonable<T>
{
    T ShallowClone();
}

public MyClass : IShallowClonable<MyClass>
{
    //TODO: define class members.

    public T ShallowClone()
    {
        return (T)MemberwiseClone(this);
    }
}

Use it like this

class Test<T> where T : IShallowClonable<T>
{
    private List<T> _listCopy;
    public Constructor(List<T> inputList)
    {
        _listCopy = new List<T>(inputList.Count);
        foreach(T obj in inputList) {
            _listCopy.Add(obj.ShallowClone());
        }
    }

    private void MakeChangesInListCopy()
    {
        foreach(T obj in _listCopy) {
            obj.ApplyChange((); // You'll need T to implement another interface or
                                // to inherit from another class for this to work.
        }
    }
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • As far as I know using clonable is not recommended according to the latest guidelines. Longer discussion - http://stackoverflow.com/questions/536349/why-no-icloneablet – Stilgar Dec 14 '14 at 17:24
  • According to this post this is "since it does not specify whether the result is a deep or a shallow copy". Well, let's call it `ShallowClone` then! – Olivier Jacot-Descombes Dec 14 '14 at 17:34
1

This is because the objects in the list are most likely reference types (classes). It is not related to the list at all it is related to the way reference types in C# behave. If you want to copy the object you should create a new object and copy the appropriate state. The best way to do this if you wrote the class yourself is to create a copy constructor.

class SomeType
{
    public string SomeState { get; set; }
    public SomeType()
    {
    }

    public SomeType(SomeType original)
    {
        this.SomeState = original.SomeState;
    }

}

Then you can copy the list in various ways. For example using LINQ

List<SomeType> copyList = originalList.Select(o => new SomeType(o)).ToList();

More importantly you should learn that when using reference types assignments do not copy the object. This is true for every usage of the type. For example variables

SomeType a = new SomeType { SomeState = "1" };
SomeType b = a;
b.SomeState = "2";

Console.WriteLine(a.SomeState); //prints "2" because both variables refer to the same object

If you do not control the type and cannot write a copy constructor you can just create a new object and copy the properties (assuming you can do that, sometimes you can't).

Stilgar
  • 22,354
  • 14
  • 64
  • 101
  • I came across copyconstructor just before you submitted your answer. Confirms that this is what i need to do :-) Learning something new every day. Thanks. – Johnson Dec 14 '14 at 17:01
  • You're a real clairvoyant. OP is asking about lists and you know he means copy custructors and me for not having a crystal ball get downvoted. – t3chb0t Dec 14 '14 at 17:05
  • @t3chb0t I teach a programming in C# course for absolute beginners. After some time doing this you develop these kind of skills :) – Stilgar Dec 14 '14 at 17:25