如果我正在处理它的副本,为什么原始值会更改
本文关键字:原始 为什么 副本 处理 如果 | 更新日期: 2023-09-27 17:57:45
我使用这个例程将一些坐标加载到容器中,绘制它们,放大它们以及诸如此类的任务。然后单击按钮,我倾向于将所有内容重置为原始或初始状态。为了做到这一点,我拿了一份首字母的副本,然后玩这个副本。但点击重置按钮,我销毁副本,取一个新的初始副本并处理它。等等。
由于某些原因,当我更改副本时,原始容器会发生更改。有人能认出我做错了什么吗?
按照顺序,我首先加载数据并复制一份:
// CoordPoint is a simple xy point
public List<CoordPoint> MyLoadedCoords { get { return myLoadedCoords; } set { myLoadedCoords = value; }}
public List<CoordPoint> MyDisplayedCoords { get { return myDisplayedCoords; } set { myDisplayedCoords = value }}
private List<CoordPoint> myLoadedCoords;
private List<CoordPoint> myDisplayedCoords;
//..
public void LoadData()
{
// load points from file
MyLoadedCoords = File.ReadLines("C:''...''Samples.txt")
// get a copy of original coords
MyDisplayedCoords = MyLoadedCoords.ToList();
}
请注意,MyLoadedCoords
不存在于代码中的任何位置(在Reset函数中,向下)。然后我在几个类似的地方处理副本MyDisplayedCoords
:
public void UpdateDisplayPosition()
{
for (var i = 0; i < MyDisplayedCoords.Count; i++)
{
MyDisplayedCoords[i].X += XCoordOffset; //some processed values
MyDisplayedCoords[i].Y += YCoordOffset; //some processed values
}
}
重置按钮我这样做:
public void ResetZoom()
{
MyDisplayedCoords = MyLoadedCoords.ToList(); // I set break point here
AdjustInitialDisplayPosition();
DrawImage();
}
ResetZoom()
没有达到预期效果,当我调试并中断MyDisplayedCoords = MyLoadedCoords;
时,我发现MyLoadedCoords
包含与MyDisplayedCoords
完全相同的值/对象
编辑:
我在类中实现了IClonable
并"覆盖"了Clone()
函数,但它确实起到了NOT的作用:
public class CoordPoint : ICloneable
{
// ..
public object Clone()
{
return new CoordPoint {X = X, Y = Y, Z = Z, Color = Color};
}
}
然而,当退出IClonable
时,这种"复制"的工作原理与H一样。B回答:
MyDisplayedCoords = MyLoadedCoords.Select(c => new CoordPoint { X = c.X, Y = c.Y, Z = c.Z, Color = c.Color }).ToList();
MyDisplayedCoords = MyLoadedCoords
不复制任何内容,它将对同一对象的引用分配给一个属性,现在两个属性都指向同一对象。
要复制列表,您可以使用Linq方法中的一个(它总是返回一个新列表):
MyDisplayedCoords = MyLoadedCoords.ToList();
要进行深度复制,您可以这样做,然后列表中的实例也会有所不同:
MyDisplayedCoords = MyLoadedCoords.Select(c =>
new CoordPoint { X = c.X, Y = c.Y }).ToList();
要添加H.B的答案,
ToList
创建了一个新的List
对象,但列表中的对象只是对同一object
的引用,除非它们是不可变的对象(例如字符串或基元类型)。
在这种情况下,您的原始List
对象不会受到影响(添加新对象/删除等),但对对象的更改将反映在两者中。因为它们指的是同一个对象。
您可以按照H.B的答案将新对象复制为新对象,也可以按照下面的过程进行,这样更干净、更正确。
在CoordPoint
类中实现ICloneable
接口。重写Clone
方法并在复制期间调用它。这是一个有点长的方法,但这样您的代码Linq
看起来会很合适。
public class CoordPoint : ICloneable
{
//rest of your code here
public object Clone()
{
return new CoordPoint
{
X= X,
Y = Y
};
}
}
那么你的Linq
代码将是,
MyDisplayedCoords = MyLoadedCoords.Select(c => (CoordPoint)c.Clone()).ToList();
当您"复制"您的列表对象时,您将获得一个新列表,但该列表中的项目仍然指向与第一个相同的项目。
解决这个问题的最简单方法是ICloneable
接口,这里有一个简短的示例:
public class MyObject : ICloneable
{
public string Property { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
用法:
var list = new List<MyObject>() { new MyObject { Property = "FirstObject" } };
var clonedList = list.Select(x => x.Clone());