C# 当对IEnumerable调用.ToList()以选择现有列表的一部分时,为什么在修改新列表时会更新原始列表?

C# 当对IEnumerable调用.ToList()以选择现有列表的一部分时,为什么在修改新列表时会更新原始列表?,c#,C#,我有一个苹果列表,上面有红色的: var redApples = apples.Where(a => a.colour == "red"); redApples在这里是一个IEnumerable,因此如果我将其转换为列表: var redApplesList = redApples.ToList(); 这给了我一个新对象的列表。因此,如果我修改这些对象: redApplesList.ForEach(a => a.color = "blue"); 我不希望我原来的苹果列表中的项目

我有一个苹果列表,上面有红色的:

var redApples = apples.Where(a => a.colour == "red");
redApples
在这里是一个
IEnumerable
,因此如果我将其转换为列表:

var redApplesList = redApples.ToList();
这给了我一个新对象的列表。因此,如果我修改这些对象:

redApplesList.ForEach(a => a.color = "blue");
我不希望我原来的
苹果
列表中的项目会改变颜色。但事实并非如此,我原来列表中的苹果是红色的,现在是蓝色的

这里的误解是什么

我的印象是
ToList()
创建了一个独立于现有列表的全新项目列表,但这表明原始列表中的指针已更新,以指向新创建的对象?这就是引擎盖下发生的事情吗


许多开发人员似乎求助于将事物分离到单独的列表中(甚至克隆对象),因为他们从来没有100%确定他们在使用什么。对这一领域有一个自信的理解是很有帮助的。

它提供了一个新的列表,但该列表包含对与原始列表相同的对象的引用(
apple
是一个类=引用类型)。它们不会被复制,因此当您通过第二个列表引用其中一个并对其进行更改时,更新的是同一个原始项

如果要复制项目,请查看,一种方法是使用
ICloneable
(Kyle评论的另一种方法是使用a)

如果您实现了这样的类:

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

    public object Clone()
    {
        return new Apple { Color = Color };
    }
}
然后检查以下内容:

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

它不会创建新对象的列表。它将创建现有对象的新列表

由于
apple
类型是引用类型,因此如果复制它,则将引用复制到
apple
对象,而不是对象本身的内容。因此,当您更改
a.color
时,您正在通过对原始对象的复制引用来更改它。“这会给我一个新对象的列表”-不,它不会。它会给你一个相同对象的新列表。请仔细阅读“浅拷贝”和“深拷贝”之间的区别。另外,请理解列表中只包含对其中实例的引用-因此,当您克隆列表时,两个列表仍然指向其中对象的相同实例。了解值/引用类型之间的差异并不能回答上述问题-错误的假设是.ToList()创建了新的引用类型。@FBryant87-实际上,它是这样的。问题的关键在于理解值类型和引用类型是如何存在和行为不同的。
ICloneable
不能保证它生成的副本是深度副本。它也不会强制您创建深度副本(事实上,由于这种模糊性,您不会在API中公开公开
ICloneable
)。我宁愿为
Apple
类实现一个复制构造函数。@Kyle-这是一个很好的注释。我并没有说每一个
ICloneable
都必须是一个深度拷贝,但我给出的片段中的一个是。
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();