2

我有一个名为粒子的对象,它有自己的属性(位置、速度等),在我的窗口窗体中,我创建了一个粒子列表。然后在代码中更新这个粒子列表(即在每个迭代步骤中更新每个粒子的位置、速度等)。

我想要做的是在每次迭代(单击按钮后)将其添加List<Particle>到另一个列表中List<List<Particle>>,这样我现在就有了可以比较的单独的粒子列表。

这就是我的代码的样子(UpdateEngine 是一个类,它在其初始化方法中创建一个粒子列表,然后具有更新其列表中粒子值的其他方法):

public partial class frmMain : Form
{
    private List<List<Particle>> listPlist;

    private UpdateEngine Engine;

    ...

    public frmMain()
    {
        InitializeComponent();

        listPlist = new List<List<Particle>>();

        Engine = new UpdateEngine();
    }

    ...

    //pressing this button iterates through a specified number of iterations
    private void btPrep_Click(object sender, EventArgs e)
    {            
        //create the particles and add the first list to the list of lists
        Engine.Initialize();
        listPlist.Add(Engine.ParticleList);

        //iterate through the list of particles in Engine and update their properties
        for(i = 0; i <= iterations; i++)
        {
            Engine.Update();
            listPlist.Add(Engine.ParticleList);
        }
    }
}

我看到发生的是第一个列表是在迭代之前添加的。在 for 循环中添加的第一个列表添加得很好。此后添加的每个列表都会将 listPlist 中的所有列表更改为与当前列表相同。

我在运行代码时看到的示例:

初始化后:

  • listPlist(0) > Particle(0) > Position = 0,0

第一次迭代后:

  • listPlist(0) > Particle(0) > Position = 0,0
  • listPlist(1) > 粒子(0) > 位置 = 1,1

下一次迭代后:

  • listPlist(0) > 粒子(0) > 位置 = 2,2
  • listPlist(1) > 粒子(0) > 位置 = 2,2
  • listPlist(2) > 粒子(0) > 位置 = 2,2

我不确定如何解决这个问题。有谁知道为什么会这样?

4

3 回答 3

3

根据我从您的问题中了解到的情况,问题在于您正在尝试复制参考项目,这就是为什么您看到所有列表项目都在更改的原因。

您将需要对这些列表/粒子进行深度克隆,以便它们不依赖于引用。

您可能必须向粒子对象添加新的复制方法或构造函数才能实现此目的。

就像是

public class Particle
{
    public int SomeField;

    public Particle Copy()
    {
        return new Particle { SomeField = this.SomeField };
    }   

    public Particle(Particle copyFrom)
    {
        this.SomeField = copyFrom.SomeField;
    }
}

然后,您可以创建原始列表的副本,例如

List<Particle> copyList = new List<Particle>(originalList.Select(c => c.Copy));
于 2012-07-25T04:34:21.220 回答
0

答案真的取决于做什么Engine.Update。如果它更新ParticleList并使用新值填充它Particle,您可以在循环内部和外部进行以下更改(复制列表):

listPlist.Add(new List<ParticleList>(Engine.ParticleList));

但是,如果它Engine.Update只是更新相同对象的内部值Particle,那么您需要通过复制列表以及Particle其他人所说的内部对象来进行深层复制。

于 2012-07-25T04:48:34.207 回答
0

您遇到的是因为List<>对象仅指向底层Particle对象。如果同一个粒子在两个(或多个)不同的列表中并且它发生了变化,那么无论您是从一个列表还是其他列表访问粒子,您都指向已更改的对象。就像说“鲍勃·史密斯”在电话簿里,也住在房子里。假设您通过去 Bob 的家割断了他的腿,如果您随后给 Bob 打电话(不是以相同的方式访问他),他仍然会失去双腿!

就像@astander 所说,您可以在Particle类中创建一个方法来创建所谓的对象的深层克隆。该方法很可能会将原始对象的所有属性(和字段)复制到一个新对象中。但是你必须小心:如果 aParticle有一个类型的字段SubParticle(或任何其他对象),当你进行深度克隆时,你可能也想对该对象进行深度克隆,否则你的两个Particles(原始和副本) 都指向同一个SubParticle.

此外,在您的特定上下文中,我建议您在循环的每次迭代之后,创建一个新的粒子副本列表。如果您随后从列表中删除了一个粒子,它只会由GarbageCollector不再被引用的粒子来处理。

编辑:通过重新阅读您的代码,我得出以下结论:在您的 Engine.Update() 方法中,您可以从创建一个新的副本列表开始。这意味着您的粒子在每次 Engine.Update() 调用之后都不是同一个对象。为此,首先实现一个返回深层克隆的 Particle.Copy() 方法,然后通过执行以下操作启动 Engine.Update():

ParticleList = new List<Particle>(ParticleList.Select(p=>p.Copy()));
于 2012-07-25T04:50:31.530 回答