Changing value of one item in a collection affects all duplicate items

假装没事ソ 提交于 2019-12-13 06:32:51

问题


I seem to be having an odd issue whereby every time I try to change a value of an item in a collection, it affects all others that contain the same initial values.

An example is below:

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

Any ideas?

Many thanks!

Update I have updated the question to include the Clone() as suggested. The output is still the same however.


回答1:


You need something like a clone method or a copy constructor.

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;

And now everything should be fine. You can use this together with your cloniung method to produce a large number of clones at once.

Just to note, this code has still a very bad taste - you are creating different product instances with equal ids. I can just guess, but do you want to build something like a shopping cart with cart items having a quantity and a product? If yes, you should really think about splitting the product class into two classes. And think about the accessibility of your properties 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; }
}

This solves your current problems because there is no longer a need for cloning products.




回答2:


Product is a reference type and your GetProducts method just yields multiple references to the same Product object.

That's why updating one instance updates any others - they're all references to the same object.




回答3:


I'm guessing it's referencing your first instance with ID=0 twice instead of two separate instances like you expect.

Try changing the ID of the third instance from 0 -> 2 and see if that 'fixes' it.




回答4:


both myProducts.ElementAt(0) and myProducts.ElementAt(1) will reference the same object.

Not sure the best way to fix this: Maybe before you add to a list, check to see if you have a reference to the object already? if you do, deep clone the object and insert it...




回答5:


After correcting a few typos in the WriteLine loop (please copy/paste working code) the error fails to reproduce, my output is:

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

After the changes:

You only create 2 instances, and therefore you are suffering from the very simple fact that you are having 3 references but only 2 instances. And the output is as it should be. It will become a little clearer if you also print the Name property.

But what you apparently want, somewhere in that very complex Enumerator/Concat story is to Clone your products.

Charlie Salts can un-delete his answer, he was right.




回答6:


Use the indexer instead.

myProducts[0].Quantity = 1



来源:https://stackoverflow.com/questions/1323133/changing-value-of-one-item-in-a-collection-affects-all-duplicate-items

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!