如果我正在处理它的副本,为什么原始值会更改

本文关键字:原始 为什么 副本 处理 如果 | 更新日期: 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());