FBryant87 FBryant87 - 1 month ago 8
C# Question

When calling .ToList() on an IEnumerable which selects part of an existing list, why is the original list updated when the new list is modified?

I have a list of

apples
, and I find those which are red:

var redApples = apples.Where(a => a.colour == "red");


redApples
is an
IEnumerable
here, so if I convert this to a list:

var redApplesList = redApples.ToList();


This gives me a list of new objects. So if I modify these objects:

redApplesList.ForEach(a => a.color = "blue");


I would not expect the items in my original
apples
list to change colour at all. But this isn't what happens, the apples in my original list which were
"red"
are now
"blue"
.

What is the misunderstanding here?

I was under the impression
ToList()
created a completely new list of items independent from existing lists, but this suggests the pointers in the original list were updated to point to the newly created objects? Is this what's happening under the hood?

A lot of developers seem to resort to separating things out into separate lists (or even cloning objects) because they're never 100% sure what they're working with. It would be helpful to have a confident understanding of this area.

Answer

It gives you a new list, but that list contains references (apple is a class = reference type) to the same objects like the original list. They are not copied so when you reference one of them via the second list and change it, it is the same original item that is updated.

If you are looking to copy the items look into deep copy and one way is by using ICloneable (another way as Kyle commented is using a copy constructor)

If you implement you class like this:

public class Apple : ICloneable
{
    public string Color { get; set; }

    public object Clone()
    {
        return new Apple { Color = Color };
    }
}

Then check this:

List<Apple> apples = new List<Apple>
{
     new Apple { Color = "red" },
     new Apple { Color = "blue" },
};

var redAppels = apples.Where(a => a.Color == "red").Select(a => (Apple)a.Clone()).ToList();
redAppels[0].Color = "green";

Console.WriteLine($"Original: {apples[0].Color}, new: {redAppels[0].Color}");
// Original red, new: green

Without the call to .Clone as in your you get the same references. With the .Clone you get new objects. Thus, when you change their Color it does not effect the original


After reading a bit more (Copy constructor versus Clone()) I'd suggest go for copy constructor instead:

public class Apple
{
    public Apple() { }
    public Apple(Apple apple)
    {
        Color = apple?.Color;
    }

    public string Color { get; set; }
}

var redAppels = apples.Where(a => a.Color == "red")
                      .Select(a => new Apple(a))
                      .ToList();
Comments