更改集合中一个项目的值会影响所有重复项目

时间:2009-08-24 15:41:58

标签: c# ienumerable

我似乎有一个奇怪的问题,每当我尝试更改集合中项目的值时,它会影响包含相同初始值的所有其他值。

以下是一个例子:

public class Product : ICloneable
{
  public int Id { get; set; }
  public string Name { get; set; }
  public int Quantity { get; set; }

  public Product()
  {
    Id = 0;
    Quantity = 0;
  }

  public Clone()
  {
    return (Product)this.MemberwiseClone();
  }
}

...

private static IEnumerable<Product> GetProducts(Product product, int quantity)
{
  for (int i = 0; i < quantity; i++)
  {
    yield return product.Clone();
  }
}

...

IEnumerable<Product> myProducts = Enumerable.Empty<Product>();
Product product1 = new Product() { Id = 0, Name = "Buzz Cola" };
Product product2 = new Product() { Id = 1, Name = "Choco Bites" };

myProducts = myProducts.Concat(GetProducts(product1, 2));
myProducts = myProducts.Concat(GetProducts(product2, 1));

//Now set the quantity of the first product to be 1.
myProducts.ElementAt(0).Quantity = 1;

foreach(Product product in myProducts)
{
  Console.WriteLine(string.Format("Id: {0}  Quantity: {1}", product.Id, product.Quantity));
}

//Output:
//Id: 0  Quantity: 1
//Id: 0  Quantity: 1 //NO!
//Id: 1  Quantity: 0

有什么想法吗?

非常感谢!

更新 我已根据建议更新了问题以包含克隆()。但输出仍然相同。

6 个答案:

答案 0 :(得分:3)

Product是一种引用类型,您的GetProducts方法只会产生对同一Product个对象的多个引用。

这就是更新一个实例更新任何其他实例的原因 - 它们都是对同一个对象的引用。

答案 1 :(得分:1)

我猜它会引用ID = 0的第一个实例两次,而不是像你期望的那样引用两个独立的实例。

尝试将第三个实例的ID从0更改为&gt; 2,看看是否“修复了”它。

答案 2 :(得分:1)

在WriteLine循环中纠正一些拼写错误(请复制/粘贴工作代码)后,错误无法重现,我的输出是:

Id: 0  Quantity: 1
Id: 1  Quantity: 0
Id: 0  Quantity: 0

更改后:

您只创建了2个实例,因此您遇到了一个非常简单的事实,即您有3个引用但只有2个实例。输出应该是应有的。如果您还打印Name属性,它会变得更清晰。

但是你明显想要的,在那个非常复杂的Enumerator / Concat故事的某个地方就是克隆你的产品。

Charlie Salts可以取消删除他的答案,他是对的。

答案 3 :(得分:1)

myProducts.ElementAt(0)和myProducts.ElementAt(1)都将引用同一个对象。

不确定解决此问题的最佳方法: 也许在添加到列表之前,请检查您是否已经引用了该对象?如果你这样做,deep clone该对象并插入它......

答案 4 :(得分:1)

您需要克隆方法或复制构造函数。

public class Product
{
  public int Id { get; set; }
  public string Name { get; set; }
  public int Quantity { get; set; }

  public Product()
  {
      this.Id = 0;
      this.Name = null;
      this.Quantity = 0;
  }

  public Product(Product product)
  {
      this.Id = product.id;
      this.Name = product.Name;
      this.Quantity = product.Quantity;
  }
}

IList<Product> myProducts = new List<Product>();

Product product1 = new Product() { Id = 0, Name = "Buzz Cola" };
Product product2 = new Product() { Id = 1, Name = "Choco Bites" };
Product product3 = new Product(product1); // Use copy-constructor.

myProducts.Add(product1);
myProducts.Add(product2);
myProducts.Add(product3);

myProducts[0].Quantity = 1;

现在一切都应该没问题。您可以将它与cloniung方法一起使用,以一次生成大量克隆。

请注意,此代码仍然非常糟糕 - 您正在使用相同的ID创建不同的产品实例。我可以猜测,但你想建立一个类似购物车的东西,购物车上有数量和产品的购物车吗?如果是,您应该考虑将产品类拆分为两个类。并考虑您的属性aganin的可访问性。

public class Product
{
  public Int32 Id { get; private set; }
  public String Name { get; private set; }
}

public class ShoppingCartItem
{
  public Product Product { get; private set; }
  public Int32 Quantity { get; set; }
}

public class ShoppingCart
{
  public IList<ShoppingCartItem> Items { get; private set; }
}

这解决了您当前的问题,因为不再需要克隆产品。

答案 5 :(得分:-1)

改为使用索引器。

myProducts[0].Quantity = 1