问题
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