列表中的对象属性在每次迭代时被覆盖

时间:2017-02-09 01:03:56

标签: c#

我构建了以下简单示例来说明我在实际代码中遇到的问题,这个问题要复杂得多。

首先,我的简单课程:

public class InnerTemp {
   public int A { get; set; }
   public int B { get; set; }
   public int C { get; set; }
}

public class OuterTemp {
   public List<InnerTemp> InnerTemps { get; set; }
}

实施:

var outerTemps = new List<OuterTemp>();
var innerTemps = new List<InnerTemp>();
innerTemps.Add(new InnerTemp() { A = 1, B = 2, C = 3 });
innerTemps.Add(new InnerTemp() { A = 4, B = 5, C = 6 });

for( int i = 1; i < 5; i++ ) {
      foreach( InnerTemp innerTemp in innerTemps ) {
           innerTemp.A += i;
           innerTemp.B += i;
           innerTemp.C += i;
       }

       var outerTemp = new OuterTemp()
       {
           InnerTemps = innerTemps
       };

       outerTemps.Add(outerTemp);
}

我在最后一行设置断点,即结束大括号。在第一次迭代中,outerTemps[0].InnerTemps[0].A的值为2,如预期的那样。但是在第二个循环中,outerTemps[0].InnerTemps[0].A的值变为4,这令我感到困惑。第一个对象的属性A仅仅因为添加了另一个对象而被踩踏。我完全希望outerTemps[1].InnerTemps[0].A有所不同,但为什么我的原始财产会被踩到?

如何更改此设置,以便每次将新对象添加到集合时,以前的对象属性都不会被覆盖?

1 个答案:

答案 0 :(得分:2)

正如我在评论中提到的那样,您正在创建innerTemps列表的浅表副本,这会导致复制引用而不是创建列表中每个InnerTemp对象的深层副本。

如果您希望在新创建的InnerTemp中单独跟踪和修改单独的OuterTemp个对象,则必须创建它们的深层副本。

根据建议here,我建议您在Copy课程中创建InnerTemp方法:

public class InnerTemp
{
   public int A { get; set; }
   public int B { get; set; }
   public int C { get; set; }

   public static InnerTemp Copy(InnerTemp source)
   {
      return new InnerTemp { A = source.A, B = source.B, C = source.C };
   }
}

在为每个OuterTemp创建新列表时使用它:

var outerTemp = new OuterTemp()
{
    InnerTemps = innerTemps.Select(InnerTemp.Copy).ToList();
};

这将阻止循环计数器修改outerTemps列表中的值。请注意,.ToList()通过评估Select枚举并返回新列表来隐式创建深层副本,因此您不必担心导致问题的列表引用。