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

FBr*_*t87 -2 c#

我有一个列表apples,我发现那些是红色的:

var redApples = apples.Where(a => a.colour == "red");
Run Code Online (Sandbox Code Playgroud)

redApples是一个IEnumerable在这里,所以如果我将其转换为列表:

var redApplesList = redApples.ToList();
Run Code Online (Sandbox Code Playgroud)

这给了我一个新对象列表.所以,如果我修改这些对象:

redApplesList.ForEach(a => a.color = "blue");
Run Code Online (Sandbox Code Playgroud)

我不希望原始apples列表中的项目根本改变颜色.但这不是发生的事情,我原来列表中的苹果"red"现在都是"blue".

这里有什么误会?

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

许多开发人员似乎都倾向于将事物分离成单独的列表(甚至是克隆对象),因为他们从不100%确定他们正在使用什么.了解这一领域将是有益的.

Gil*_*een 6

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

如果您希望将项目复制到深层复制中,一种方法是使用ICloneable(另一种方式是Kyle评论使用的是复制构造函数)

如果你实现这样的类:

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

    public object Clone()
    {
        return new Apple { Color = Color };
    }
}
Run Code Online (Sandbox Code Playgroud)

然后检查一下:

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
Run Code Online (Sandbox Code Playgroud)

没有你的电话,.Clone你得到相同的参考.随着.Clone你得到新的对象.因此,当你改变它们时Color它不会影响原始


在阅读了一下之后(复制构造函数与克隆())我建议去复制构造函数:

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();
Run Code Online (Sandbox Code Playgroud)