FBryant87 FBryant87 - 10 months ago 39
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

, and I find those which are red:

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

is an
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
list to change colour at all. But this isn't what happens, the apples in my original list which were
are now

What is the misunderstanding here?

I was under the impression
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 Source

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))