重用对象会导致List <t>冗余</t>

时间:2013-04-02 21:50:21

标签: c#

我遇到了一个问题,我在IQueryable对象中返回数据并重复了。我通过将用于构建集合的临时对象从方法作用域移动到循环作用域来解决它。换句话说,每次循环重复时它都会消失。

原件:

private void MyMethod()
{
  SomeObj ProjDtl = new SomeObj(); //here is the obj method scoped


  foreach (ReservedSimProjects item in parameters.ReservedProjects.ReservedSimProj)
  {

    IQueryable<SJ> DbProj = SimColl.Where(e => e.SimProjectID == item.ProjectId);

     ProjDtl.ProjName = DbProj.First().ProjName;
     ProjDtl.ProjArea = DbProj.First().ProjArea;
     ProjDtl.ProjPerim = DbProj.First().ProjArea;
     ....etc....

     ProjectsDetails.Add(ProjDtl);
  }

现在我明显不正确的想法ProjDtl将被创建一次并重复使用。循环迭代时,旧值将被新值覆盖。而这似乎正是发生的事情。逐步调试调试器,ProjDtl属性被覆盖,但最后一行被添加到集合中,第一遍的原始内容被添加,因此我的集合有重复,三次重复等。更改范围,以便每次传递都重新创建对象问题。为什么因为有一些指向原始对象的指针并且它已经被更新,即使它上面的每个属性都被重新分配了???

private void MyMethod()
{

  foreach (ReservedSimProjects item in parameters.ReservedProjects.ReservedSimProj)
  {

     SomeObj ProjDtl = new SomeObj(); //here is the obj method scoped 

     IQueryable<SJ> DbProj = SimColl.Where(e => e.SimProjectID == item.ProjectId);

     ProjDtl.ProjName = DbProj.First().ProjName;
     ProjDtl.ProjArea = DbProj.First().ProjArea;
     ProjDtl.ProjPerim = DbProj.First().ProjArea;
     ....etc....

     ProjectsDetails.Add(ProjDtl);
  }

所以,一旦我设法解决了我自己的问题,但我不确定为什么/解决了什么。

JB

5 个答案:

答案 0 :(得分:4)

使用new创建对象时,内存中会留有一些空间。用于访问对象的变量不直接存储对象,而是指向对象位置的指针。

SomeObj ProjDtl = new SomeObj();

RAM:
+--------------------+-----------------------------+
| 0x12345            | 0xabcdef                    |
+--------------------+-----------------------------+
| ProjDtl = 0xabcdef | [data for a SomeObj object] |
+--------------------+-----------------------------+

在原始循环中,您有多个变量,但每个变量都指向内存中的相同地址并使用相同的实际对象:

SomeObj ProjDtl = new SomeObj(); //now ProjDtl points to 0xabcdef
foreach (ReservedSimProjects item in parameters.ReservedProjects.ReservedSimProj)
  {
     IQueryable<SJ> DbProj = SimColl.Where(e => e.SimProjectID == item.ProjectId);

     ProjDtl.ProjName = DbProj.First().ProjName; //Since ProjDtl is never overwritten,
     ProjDtl.ProjArea = DbProj.First().ProjArea; //it will always point to 0xabcdef,
     ProjDtl.ProjPerim = DbProj.First().ProjArea; //and you're always accessing the same physical object's data
     ....etc....

     ProjectsDetails.Add(ProjDtl);
  }

在更正后的版本中,每次在循环中创建一个新对象时,都会在内存中为新对象留出更多空间并改为修改其细节。

private void MyMethod()
{

  foreach (ReservedSimProjects item in parameters.ReservedProjects.ReservedSimProj)
  {
     SomeObj ProjDtl = new SomeObj(); //On the first iteration, ProjDtl might point to 0xabcdef,
     //on the second, 0x98765, on the third, 0xfedcba... but it will always be a new, unused memory address

     IQueryable<SJ> DbProj = SimColl.Where(e => e.SimProjectID == item.ProjectId);

     ProjDtl.ProjName = DbProj.First().ProjName; //Now, each iteration modifies a different
     ProjDtl.ProjArea = DbProj.First().ProjArea; //physical object
     ProjDtl.ProjPerim = DbProj.First().ProjArea;
     ....etc....

     ProjectsDetails.Add(ProjDtl);
  }

答案 1 :(得分:3)

  

为什么因为存在某种指向原始对象的指针而且即使它上面的每个属性都被重新分配它还没有被更新?

没错。

您已创建了一个对象。您可以根据需要多次设置该对象上的所有属性,但它仍然是同一个对象。

每次调用ProjectDetails.Add(ProjDtl)时,都会将相同的对象添加到列表中。

该列表不存储对象属性的值 - 它存储对象本身的单个引用。

您的固定示例有效,因为您每次在循环周围创建一个新对象。然后设置该特定对象的属性并将其添加到列表中。

这意味着列表最终会保存对许多不同对象的引用,每个对象都有自己的属性值。

答案 2 :(得分:1)

是的,“指向原始对象的指针”实际上是你的变量。 ProjectsDetails.Add在每次迭代时将引用添加到列表中。但由于它是相同的变量,它是相同的对象!所以你要做的就是在循环中一遍又一遍地覆盖那个实例的属性。如果您的列表长度为N个值,那么您将拥有一个N个项目列表,这些项目都指向同一个实例。

答案 3 :(得分:1)

你不能像那样重复使用你的对象,因为它会尝试多次保存同一个对象。

您可以重复使用变量,但不能重复对象

private void MyMethod()
{
  SomeObj ProjDtl = null;


  foreach (ReservedSimProjects item in parameters.ReservedProjects.ReservedSimProj)
  {
     ProjDtl = new SomeObj()
     IQueryable<SJ> DbProj = SimColl.Where(e => e.SimProjectID == item.ProjectId);

     ProjDtl.ProjName = DbProj.First().ProjName;
     ProjDtl.ProjArea = DbProj.First().ProjArea;
     ProjDtl.ProjPerim = DbProj.First().ProjArea;
     ....etc....

     ProjectsDetails.Add(ProjDtl);
  }

答案 4 :(得分:0)

  

为什么因为存在某种指向原始对象的指针而且即使它上面的每个属性都被重新分配它还没有被更新?

是 - 非原始类型和非结构类型是“引用类型” - 这意味着当您创建该类型的变量时,您正在为该实例创建引用(指针),因此重新分配循环中的值实际上每次都影响相同的实例

您的解决方案是正确的。