当在IEnumerable上调用. tolist()时,它选择了现有列表的一部分,为什么在修改新列表时更新原始列表?
本文关键字:列表 为什么 一部分 修改 新列表 更新 原始 调用 IEnumerable tolist 当在 | 更新日期: 2023-09-27 18:15:58
我有一个apples
的列表,我找到了那些红色的:
var redApples = apples.Where(a => a.colour == "red");
redApples
在这里是一个IEnumerable
,所以如果我把它转换成一个列表:
var redApplesList = redApples.ToList();
这给了我一个新对象的列表。如果我修改这些对象
redApplesList.ForEach(a => a.color = "blue");
我不希望我原来的apples
列表中的项目完全改变颜色。但事实并非如此,我原来列表中"red"
的苹果现在变成了"blue"
。
这里的误解是什么?
我的印象是ToList()
创建了一个独立于现有列表的全新列表,但这表明原始列表中的指针被更新为指向新创建的对象?这就是引擎盖下发生的事吗?
许多开发人员似乎求助于将东西分离到单独的列表(甚至克隆对象),因为他们从来没有100%确定他们正在处理什么。对这个领域有一个自信的了解是很有帮助的。
它给出了一个新的列表,但是该列表包含对与原始列表相同对象的引用(apple
是一个class=引用类型)。它们没有被复制,所以当你通过第二个列表引用其中一个并改变它时,它是被更新的相同的原始项。
如果你想复制项目,查看深度复制,一种方法是使用ICloneable
(另一种方法是使用复制构造函数)
如果你像这样实现你的类:
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
如果没有调用.Clone
,您将获得相同的引用。使用.Clone
,您可以获得新对象。因此,当您更改它们的Color
时,它不会影响原来的
在阅读了更多(复制构造函数与Clone())之后,我建议使用复制构造函数:
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();
它不创建新对象的列表。它创建现有对象的新列表。