Изменение значения одного элемента в коллекции влияет на все повторяющиеся элементы - PullRequest
0 голосов
/ 24 августа 2009

Кажется, у меня возникла странная проблема, из-за которой каждый раз, когда я пытаюсь изменить значение элемента в коллекции, это влияет на все остальные, содержащие одинаковые начальные значения.

Пример ниже:

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 ]

3 голосов
/ 24 августа 2009

Product является ссылочным типом, а ваш метод GetProducts просто возвращает несколько ссылок на один и тот же объект Product.

Вот почему обновление одного экземпляра обновляет все остальные - все они ссылаются на один и тот же объект.

1 голос
/ 24 августа 2009

Вам нужно что-то вроде метода клонирования или конструктора копирования.

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;

А теперь все должно быть хорошо. Вы можете использовать это вместе со своим методом клонирования для одновременного производства большого количества клонов.

Просто чтобы заметить, этот код все еще очень плохой - вы создаете разные экземпляры продукта с одинаковыми идентификаторами. Я могу только догадываться, но хотите ли вы создать что-то вроде корзины для покупок из товаров с количеством и товаром? Если да, вам стоит подумать о разделении класса продукта на два класса. И подумайте о доступности ваших свойств аганина.

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; }
}

Это решает ваши текущие проблемы, потому что больше нет необходимости в клонировании продуктов.

1 голос
/ 24 августа 2009

и myProducts.ElementAt (0), и myProducts.ElementAt (1) ссылаются на один и тот же объект.

Не уверен, что лучший способ это исправить: Может быть, прежде чем добавить в список, проверьте, есть ли у вас ссылка на объект уже? если вы сделаете, глубоко клонируйте объект и вставьте его ...

1 голос
/ 24 августа 2009

После исправления нескольких опечаток в цикле WriteLine (пожалуйста, скопируйте / вставьте рабочий код) ошибку не удается воспроизвести, мой вывод:

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

После изменений:

Вы создаете только 2 экземпляра, и поэтому вы страдаете от очень простого факта, что у вас есть 3 ссылки, но только 2 экземпляра. И вывод такой, каким должен быть. Это станет немного понятнее, если вы также напечатаете свойство Name.

Но то, что вы, очевидно, хотите, где-то в этой очень сложной истории Enumerator / Concat, - это клонирование ваших продуктов.

Чарли Солтс может удалить свой ответ, он был прав.

1 голос
/ 24 августа 2009

Я предполагаю, что он ссылается на ваш первый экземпляр с ID = 0 дважды, а не на два отдельных экземпляра, как вы ожидаете.

Попробуйте изменить идентификатор третьего экземпляра с 0 -> 2 и посмотрите, исправляет ли это это.

0 голосов
/ 24 августа 2009

Вместо этого используйте индексатор.

myProducts[0].Quantity = 1

...