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

C# 更改集合中一项的值会影响所有重复项,c#,ienumerable,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 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
公共类产品:iClonable
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共整数数量{get;set;}
公共产品()
{
Id=0;
数量=0;
}
公共克隆()
{
返回(产品)this.MemberwiseClone();
}
}
...
私有静态IEnumerable GetProducts(产品产品,整数数量)
{
对于(int i=0;i
有什么想法吗

非常感谢


更新我已更新了问题,将建议的克隆()包括在内。但是输出仍然相同。

我猜它引用的是ID为0的第一个实例两次,而不是像您期望的那样两个单独的实例


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

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

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

更改后:

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

但在这个非常复杂的枚举器/Concat故事中,您显然想要克隆您的产品


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

使用索引器


myProducts[0]。Quantity=1
Product
是一种引用类型,您的
GetProducts
方法只会生成对同一
Product
对象的多个引用

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

myProducts.ElementAt(0)和myProducts.ElementAt(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;
公共类产品
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共整数数量{get;set;}
公共产品()
{
这个.Id=0;
this.Name=null;
这个。数量=0;
}
公共产品(产品)
{
this.Id=product.Id;
this.Name=product.Name;
此.数量=产品.数量;
}
}
IList myProducts=新列表();
product1=新产品(){Id=0,Name=“Buzz Cola”};
Product product2=新产品(){Id=1,Name=“Choco-Bites”};
产品3=新产品(产品1);//使用复制构造函数。
myProducts.Add(product1);
myProducts.Add(product2);
myProducts.Add(product3);
myProducts[0]。数量=1;
现在一切都好了。您可以将此方法与cloniung方法一起使用,一次生成大量克隆

需要注意的是,这段代码的味道仍然很差——您正在使用相同的ID创建不同的产品实例。我只能猜测,但你想建立一个类似购物车的东西,购物车上的物品有数量和产品吗?如果是,您应该真正考虑将product类拆分为两个类。再考虑一下您的属性的可访问性

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; }
}
公共类产品
{
公共Int32 Id{get;私有集;}
公共字符串名称{get;private set;}
}
公共类购物车项目
{
公共产品产品{get;private set;}
公共Int32数量{get;set;}
}
公共类购物车
{
公共IList项{get;private set;}
}

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

您如何更改项目?myProducts.ElementAt(0)。Quantity=1
myProducts
没有
Is
Quantity
属性。你是说
product.Id
?我也试过做一个循环,每增加一个数量,结果Buzz可乐的数量是2!您添加了两次相同的产品,因此只有一个实例,列表中的两项都可以看到更改。这样做会将第一项设置为2。那么您问题中的示例不会反映您的实际代码。您的示例按预期工作并产生预期数量。我已重新编写了代码。它没有反映实际的代码,你是对的。问题是我错过了两次传递相同对象引用的GetProducts()。IEnumerable没有提供索引器。我可以发誓他使用的是列表。很抱歉代码似乎正在更改。Dan以前使用过一个列表,但该变量从一开始就是IEnumerable类型,因此如果没有向下转换,您无法访问索引器。我重写了问题,因为我没有反映真实的代码。@Henk:我用克隆()更新了问题。但是,结果是一样的。Dan,最好先想想你真正想要的是什么,Clone()和IClonable充满了陷阱,当然对于那些仍在挣扎于值/引用类型的人来说。问: