如果我正在处理它的副本,为什么原始值会发生变化?

时间:2016-07-19 16:18:00

标签: c# wpf list logic

我正在使用此例程将一些坐标加载到容器中,绘制它们,放大它们以及这些任务。然后在按钮单击时,我倾向于重置所有内容到原始状态或初始状态。要做到这一点,我拿一份初始版本并播放副本。但是在重置按钮单击时,我销毁副本,获取初始副本并处理它。等等。

由于某种原因,我更改副本时原始容器会更改。谁能发现我做错了什么?

按顺序,我先加载数据并复制一份:

//          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存在 no where (并且在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()功能,但它确实工作:

public class CoordPoint : ICloneable
{
    // .. 

    public object Clone()
    {
        return new CoordPoint {X = X, Y = Y, Z = Z, Color = Color};
    }
}

但是, out IClonable此“复制”工作克隆为H.B已回答:

MyDisplayedCoords = MyLoadedCoords.Select(c => new CoordPoint { X = c.X, Y = c.Y, Z = c.Z, Color = c.Color }).ToList();

3 个答案:

答案 0 :(得分:5)

MyDisplayedCoords = MyLoadedCoords不会复制任何内容,它会将同一个对象的引用分配给属性,现在这两个属性都指向同一个对象。

要复制可以使用Linq方法的列表(它总是返回一个新列表):

MyDisplayedCoords = MyLoadedCoords.ToList();

要进行深层复制,您可以执行类似的操作,然后列表中的实例也会有所不同:

MyDisplayedCoords = MyLoadedCoords.Select(c =>
                                       new CoordPoint { X = c.X, Y = c.Y }).ToList();

答案 1 :(得分:2)

添加H.B的答案,

ToList创建一个新的List对象,但列表中的对象只是对同一object的引用,除非它们是不可变对象(例如字符串或原始类型) )。

在这种情况下,您的原始List对象不会受到影响(添加新对象/删除等),但对象的更改将反映在两者中。因为他们引用相同的对象。

您可以按照H.B的回答将新复制品作为新对象复制,也可以按照以下流程进行更清晰,更合理。

ICloneable课程中实施CoordPoint界面。覆盖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();

答案 2 :(得分:0)

当你复制&#39;您的列表对象是一个新的列表,但该列表中的项目仍然指向与第一个相同的项目。

解决此问题的最简单方法是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());