Search code examples
c#genericscollectionsdeep-copy

Modify new collection without touching the original one


My goal is to get a copy of collection with specified item removed from it without touching the items of original collection. I have the following class:

public class Foo
{
    public string Name { get; set; }
}

and the operation I am doing is:

 var collection = new Collection<Foo>
                         {
                             new Foo {Name = "Name1"},
                             new Foo {Name = "Name2"},
                             new Foo {Name = "Name3"},
                             new Foo {Name = "Name4"}
                         };
    var newCollection = new Collection<Foo>(collection);

    Foo f = collection.FirstOrDefault(x => x.Name == "Name2");
    if (f != null)
    {
        newCollection.Remove(f);
    }

i.e I am removing the item from "newCollection" but the issue is that the following line:

newCollection.Remove(f);

is removing the item from original collection too i.e from "collection" object. I want to modify "newCollection" only and not "collection". How can I do that? Isn't the following line doing a deep copy:

 var newCollection = new Collection<Foo>(collection);

if so, then why the original object is affected?

I know I can achieve my goal via this line too:

var newCollection = collection.Where(x => x.Name != "Name2");

but I am in dilemma about the Remove stuff and deep copy stuff happening above.


Solution

  • This is because of the behaviour of the Collection<T>(IList<T>) constructor:

    The elements of the list are not copied. The list is wrapped by the collection, so that subsequent changes to the elements of the list are visible through the Collection.

    If you want a shallow copy of the collection1, you could use List<T> instead:

    List<Foo> newCollection = new List<T>(collection);
    

    (It's a little odd to see Collection<T> used like this at all. It's normally used as the base class for other generic collections.)


    1 It's a shallow copy because it's just copying the value of each element as a reference, rather than cloning each Foo object. If you wrote:

    newCollection[0].Name = "Hello!";
    Console.WriteLine(collection[0]);
    

    ... it would still print "Hello!". Creating a wrapper around an existing collection isn't really creating a copy at all, shallow or deep.