【问题标题】:Why original value changes if I am processing a copy of it?如果我正在处理它的副本,为什么原始值会发生变化?
【发布时间】:2016-07-19 16:17:59
【问题描述】:

我正在使用这个例程将一些坐标加载到容器中,绘制它们,放大它们以及这些任务。然后单击按钮,我倾向于将所有内容重置到原始或初始状态。为此,我会复制一份初始副本并使用该副本。但是在单击重置按钮时,我会销毁副本,获取初始副本的新副本并进行处理。以此类推。

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

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

//          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};
    }
}

但是,outIClonable这种“复制”工作就像克隆H.B 回答:

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

【问题讨论】:

  • 您没有得到副本,您实际上是在处理一个具有所有相同数据指针的新列表。要解决这个问题,您需要使用不支持嵌套引用类型的 Clone,或者实现您自己的深度克隆。
  • @FirstStep, CoordPoint, 是类还是结构?因为如果它是一个类,.ToList() 将创建包含相同引用的新列表。
  • @ArtavazdBalayan 这是一门课。有趣的是我不知道,所以如果它是一个结构,它可以工作吗?
  • @FirstStep,这是一个mini-example。可以看到Lists有不同的hashcode,但是它们的内容是一样的。
  • @ArtavazdBalayan 我明白了。谢谢我今天学到了新东西

标签: c# wpf list logic


【解决方案1】:

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

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

MyDisplayedCoords = MyLoadedCoords.ToList();

要进行深度复制,您可以这样做,然后列表中的实例也会有所不同:

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

【讨论】:

  • 我最初是在做这个MyDisplayedCoords = MyLoadedCoords.ToList(),所以它应该分配一个副本号?
  • 是的,但是您的 ResetZoom 没有这样做,从那时起您将拥有相同的对象,也许您在某个地方调用它?
  • 那么你需要按照毗湿奴的建议进行深度复制。
  • @FirstStep:添加了一个深度复制的方法(如果 X 和 Y 是唯一的属性并且你有一个空的构造函数,否则相应地调整它)。
  • 嗨,我还有几个额外的属性 ColorZ 现在只是零。我有两个构造函数:一个带有 All 私有属性的参数,另一个没有任何参数,但它将 All 属性设置为内部的一些默认值。在应用您的解决方案之前我需要更改任何内容吗?
【解决方案2】:

要补充 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();

【讨论】:

  • “除非它们是不可变对象”应该是“除非它们是值类型”。
  • 嗨,我该如何解决这个问题?
  • @FirstStep 请检查我的解决方案是否适合您
【解决方案3】:

当您“复制”列表对象时,您将获得一个新列表,但该列表中的所有项目仍指向与第一个相同的项目。

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

【讨论】:

    猜你喜欢
    • 2021-04-16
    • 1970-01-01
    • 2013-05-13
    • 1970-01-01
    • 1970-01-01
    • 2019-07-27
    • 1970-01-01
    • 2022-01-07
    相关资源
    最近更新 更多