2009-08-24 8 views
0

Je semblons avoir un problème étrange dans lequel chaque fois que je tente de modifier une valeur d'un élément dans une collection, il affecte tous les autres qui contiennent les mêmes valeurs initiales.Changement de valeur d'un élément dans une collection affecte tous les éléments en double

Un exemple est ci-dessous:

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 

Toutes les idées?

Un grand merci!

Mise à jour J'ai mis à jour la question d'inclure le clone() comme l'a suggéré. La sortie est toujours la même cependant.

+0

Comment changez-vous l'article? –

+0

myProducts.ElementAt (0) .Quantité = 1; –

+0

'myProducts' n'a pas de propriété' Is' ou 'Quantity'. Voulez-vous dire 'product.Id'? – dtb

Répondre

1

Vous avez besoin quelque chose comme une méthode clone ou un constructeur de copie.

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; 

Et maintenant tout devrait bien se passer. Vous pouvez l'utiliser avec votre méthode cloniung pour produire un grand nombre de clones à la fois.Juste pour noter, ce code a toujours un très mauvais goût - vous créez différentes instances de produit avec des ID égaux. Je peux juste deviner, mais voulez-vous construire quelque chose comme un panier avec des articles de chariot ayant une quantité et un produit? Si oui, vous devriez vraiment penser à diviser la classe de produit en deux classes. Et pensez à l'accessibilité de vos propriétés 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; } 
} 

Cela résout vos problèmes actuels car il n'y a plus besoin de produits de clonage.

+0

Daniel, j'ai mis à jour le GetProducts() avec \t \t \t \t rendement de retour nouveau Produit (produit); et ont créé un constructeur de copie, mais le résultat est toujours le même.Je mets à jour un, et tous les deux obtiennent leurs quantités changées. –

+0

Le problème est le suivant. Lorsque la nouvelle quantité est définie, la requête est évaluée et un nouveau produit est créé en clonant ou en appelant le constructeur de copie et le nouveau produit est modifié. Lorsque la collection est imprimée, la requête est réévaluée et le produit est cloné ou reconstruit à nouveau, par conséquent la mise à jour est perdue et toutes les quantités sont nulles. C'est un bug différent de celui d'origine. –

1

Je devine que c'est le référencement de votre première instance avec ID = 0 deux fois au lieu de deux instances distinctes comme vous attendez.

Essayez de changer l'ID de la troisième instance de 0 -> 2 et voir si ce fixe 'elle.

+0

Ceci définit également le premier à 2. –

+0

Ensuite, votre exemple dans votre question ne reflète pas votre code actuel. Votre exemple fonctionne comme prévu et génère les quantités attendues. – dtb

+0

J'ai refait le code. Cela n'a pas reflété le code réel, vous avez raison. Le problème était que j'avais manqué le GetProducts() en passant deux fois la même référence d'objet. –

1

Après avoir corrigé quelques fautes de frappe dans la boucle de WriteLine (s'il vous plaît copier/coller le code de travail) l'erreur ne parvient pas à reproduire, ma sortie est:

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

Après les changements:

Vous seulement créer 2 instances, et donc vous souffrez du fait très simple que vous avez 3 références mais seulement 2 instances. Et la sortie est comme il se doit. Cela deviendra un peu plus clair si vous imprimez également la propriété Name.

Mais apparemment ce que vous voulez, quelque part dans ce recenseur très complexe/histoire Concat est de cloner vos produits. Charlie Salts peut annuler sa réponse, il avait raison.

+0

Je suis également incapable de reproduire l'erreur. –

+0

J'ai réécrit la question, car je n'ai pas reflété le vrai code. –

+0

@Henk: J'ai mis à jour la question avec le Clone(). Le résultat est cependant le même. –

-1

Utilisez plutôt l'indexeur.

myProducts[0].Quantity = 1

+0

Quelle est la différence? – Gavin

+0

IEnumerable ne fournit pas d'indexeur. –

+0

J'aurais juré qu'il utilisait une liste. Pardon. Le code semble changer. – Chrisb

3

Product est un type de référence et votre méthode GetProducts cède simplement plusieurs références au même objet Product. C'est pourquoi la mise à jour d'une instance met à jour toutes les autres - ce sont toutes des références au même objet.

+0

Y a-t-il un moyen de changer cette référence afin qu'elle ne concerne qu'un seul? –

+0

Vous pouvez soit cloner l'objet Product dans la méthode GetProducts, soit déclarer Product en tant que struct plutôt que comme une classe. –

+0

J'ai modifié GetProducts() pour qu'il fonctionne: yield return product.Clone(); et ont ajouté le Clone() au Produit avec retour (Product) this.MemberwiseClone(); Hélas, le résultat est toujours le même. –

1

deux myProducts.ElementAt (0) et myProducts.ElementAt (1) fera référence au même objet.

Vous ne savez pas comment résoudre ce problème: Avant de vous ajouter à une liste, vérifiez si vous connaissez déjà l'objet. si vous le faites, deep clone l'objet et l'insérer ...

Questions connexes