Question

J'ai une liste de structures et je souhaite modifier un élément.Par exemple :

MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");

Maintenant, je veux changer un élément :

MyList[1].Name = "bob"

Cependant, chaque fois que j'essaie de le faire, j'obtiens l'erreur suivante :

Impossible de modifier la valeur de retour de System.collections.generic.list.Ce [int] 'parce que ce n'est pas une variable

Si j'utilise une liste de classes, le problème ne se produit pas.

Je suppose que la réponse est liée au fait que les structures sont un type valeur.

Donc, si j'ai une liste de structures, dois-je les traiter comme lecture seulement?Si je dois modifier des éléments dans une liste, dois-je utiliser des classes et non des structures ?

Était-ce utile?

La solution

MyList[1] = new MyStruct("bob");

les structures en C# doivent presque toujours être conçues pour être immuables (c'est-à-dire qu'elles n'ont aucun moyen de modifier leur état interne une fois qu'elles ont été créées).

Dans votre cas, ce que vous voulez faire est de remplacer la structure entière dans l'index de tableau spécifié, et non d'essayer de modifier une seule propriété ou un seul champ.

Autres conseils

Pas assez.La conception d'un type en tant que classe ou structure ne devrait pas être motivée par votre besoin de le stocker dans des collections :) Vous devriez examiner la « sémantique » nécessaire

Le problème que vous voyez est dû à la sémantique du type valeur.Chaque variable/référence de type valeur est une nouvelle instance.Quand tu dis

Struct obItem = MyList[1];

ce qui se passe, c'est qu'une nouvelle instance de la structure est créée et que tous les membres sont copiés un par un.Pour que vous ayez un clone de MyList[1], c'est-à-dire2 cas.Maintenant, si vous modifiez obItem, cela n'affecte pas l'original.

obItem.Name = "Gishu";  // MyList[1].Name still remains "peter"

Maintenant, restez avec moi pendant 2 minutes ici (cela prend un certain temps à avaler...C'est ce que je l'ai fait :) Si vous avez vraiment besoin de structures pour être stockées dans une collection et modifiées comme vous l'avez indiqué dans votre question, vous devrez faire en sorte que votre structure exposait une interface (Cependant cela entraînera la boxe).Vous pouvez ensuite modifier la structure réelle via une référence d'interface, qui fait référence à l'objet encadré.

L'extrait de code suivant illustre ce que je viens de dire ci-dessus

public interface IMyStructModifier
{
    String Name { set; }
}
public struct MyStruct : IMyStructModifier ...

List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));

MyStruct temp = (MyStruct)obList[1];
temp.Name = "Gishu";
foreach (MyStruct s in obList) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}

IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name = "Now Gishu";
foreach (MyStruct s in obList) // => "ABC", "Now Gishu"
{
    Console.WriteLine(s.Name);
}

HTH.Bonne question.
Mise à jour: @Hath - vous m'avez fait courir pour vérifier si j'avais oublié quelque chose d'aussi simple.(Ce serait incohérent si les propriétés du setter ne le faisaient pas et les méthodes le faisaient - l'univers .Net est toujours équilibré :)
La méthode Setter ne fonctionne pas
obList2[1] renvoie une copie dont l'état serait modifié.La structure originale de la liste reste inchangée.Donc Set-via-Interface semble être le seul moyen de le faire.

List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}

Ce n’est pas tant que les structures sont « immuables ».

Le véritable problème sous-jacent est que les structures sont un type Valeur, pas un type Référence.Ainsi, lorsque vous extrayez une « référence » à la structure de la liste, cela crée une nouvelle copie de la structure entière.Ainsi, toutes les modifications que vous y apportez modifient la copie, et non la version originale dans la liste.

Comme le dit Andrew, vous devez remplacer la structure entière.À ce stade, je pense que vous devez vous demander pourquoi vous utilisez une structure en premier lieu (au lieu d'une classe).Assurez-vous de ne pas le faire en raison de problèmes d'optimisation prématurés.

Il n'y a rien de mal avec les structures qui ont des champs exposés ou qui permettent une mutation via des définisseurs de propriétés.Les structures qui mutent elles-mêmes en réponse à des méthodes ou à des getters de propriétés sont cependant dangereuses car le système permettra aux méthodes ou aux getters de propriétés d'être appelés sur des instances de structure temporaires ;si les méthodes ou les getters apportent des modifications à la structure, ces modifications finiront par être ignorées.

Malheureusement, comme vous le remarquez, les collections intégrées à .net sont vraiment incapables d'exposer les objets de type valeur qu'elles contiennent.Votre meilleur pari est généralement de faire quelque chose comme :

  MyStruct temp = myList[1];
  temp.Name = "Albert";
  myList[1] = temp;

Un peu ennuyeux, et pas du tout threadsafe.Encore une amélioration par rapport à une liste de type classe, où faire la même chose peut nécessiter :

  myList[1].Name = "Albert";

mais cela peut aussi nécessiter :

  myList[1] = myList[1].Withname("Albert");

ou peut-être

  myClass temp = (myClass)myList[1].Clone();
  temp.Name = "Albert";
  myList[1] = temp;

ou peut-être une autre variante.On ne pourrait vraiment pas le savoir à moins d'examiner myClass ainsi que l'autre code qui met les éléments dans la liste.Il est tout à fait possible que l'on ne puisse pas savoir si le premier formulaire est sûr sans examiner le code des assemblys auxquels on n'a pas accès.En revanche, si Name est un champ exposé de MyStruct, la méthode que j'ai donnée pour le mettre à jour fonctionnera, indépendamment de ce que contient d'autre MyStruct, ou indépendamment de ce que d'autres choses ont pu faire avec myList avant l'exécution du code ou de ce à quoi ils peuvent s'attendre. faire avec ça après.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top