c# modifiant les structures dans une Liste<T>
Petite question: Comment puis-je modifier des éléments individuels dans une List
? (ou, plus précisément, les membres d'un struct
stockées dans un List
?)
Explication complète:
Tout d'abord, le struct
définitions des termes utilisés ci-dessous:
public struct itemInfo
{
...(Strings, Chars, boring)...
public String nameStr;
...(you get the idea, nothing fancy)...
public String subNum; //BTW this is the element I'm trying to sort on
}
public struct slotInfo
{
public Char catID;
public String sortName;
public Bitmap mainIcon;
public IList<itemInfo> subItems;
}
public struct catInfo
{
public Char catID;
public String catDesc;
public IList<slotInfo> items;
public int numItems;
}
catInfo[] gAllCats = new catInfo[31];
gAllCats
est renseigné sur la charge, et ainsi de suite jusqu'à la ligne comme le programme s'exécute.
Le problème se pose lorsque je veux trier les itemInfo
objets dans le subItems
tableau.
Je suis à l'aide de LINQ pour ce faire (car il ne semble pas y avoir d'autre moyen raisonnable de trier les listes de non-intégré-type).
Alors, voici ce que j'ai:
foreach (slotInfo sInf in gAllCats[c].items)
{
var sortedSubItems =
from itemInfo iInf in sInf.subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
sInf.subItems.Clear();
sInf.subItems = sortedSubTemp; //ERROR: see below
}
De l'erreur "Impossible de modifier les membres de la "sInf" parce que c'est un "foreach variable d'itération'".
un, cette restriction n'a pas de sens; ce n'est pas l'utilisation principale de la structure foreach?
b (de dépit) qu'est-Clear() faire si pas de modifier la liste? (BTW, la Liste est effacée, selon le débogueur, si je supprime la dernière ligne et de l'exécuter.)
J'ai donc essayé de prendre une approche différente, et voir si cela a fonctionné, à l'aide d'un régulier pour la boucle. (Apparemment, c'est uniquement autorisée parce que gAllCats[c].items
est en fait un IList
; je ne pense pas que cela va vous permettre d'index régulièrement List
de cette façon.)
for (int s = 0; s < gAllCats[c].items.Count; s++)
{
var sortedSubItems =
from itemInfo iInf in gAllCats[c].items[s].subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo>();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
//NOTE: the following two lines were incorrect in the original post
gAllCats[c].items[s].subItems.Clear();
gAllCats[c].items[s].subItems = sortedSubTemp; //ERROR: see below
}
Cette fois, l'erreur est "Impossible de modifier la valeur de retour de 'Système.Les Collections.Génériques.IList.cette[int] " parce qu'il n'est pas une variable." Pouah! Qu'est-ce, si ce n'est une variable? et quand est-elle devenue une "valeur de retour'?
Je sais qu'il y a à être une "bonne" façon de le faire; je viens à ce à partir d'un C en arrière-plan et je sais que je pourrais le faire en C (mais avec un bon morceau de manuel de gestion de la mémoire.)
J'ai cherché partout, et il semble que ArrayList
a passé de mode en faveur de types génériques (je suis en utilisant 3.0) et je ne peux pas utiliser un tableau de la taille doit être dynamique.
OriginalL'auteur andersop | 2009-07-01
Vous devez vous connecter pour publier un commentaire.
En regardant la boucle for approche, la raison (et la solution) pour ce qui est donné dans le documentation pour l'erreur de compilation:
Donc, dans ta boucle for, modifiez les lignes suivantes:
...:
J'ai supprimé l'appel à la
Clear
méthode, car je ne pense pas que ça ajoute quoi que ce soit.merci de souligner ce point. Je savais à propos de ce que ce que la modification de la propriété d'une structure de propriété, mais je ne savais pas qu'il applique pour générique collections. J'ai simplement supposé que la Liste elle-même était une classe, il est donc de l'indexeur doit retourner une référence À la structure. J'imagine que la façon dont il fonctionne avec les non-générique collections. merci encore
Viens de trouver cette solution. Fonctionne merci!
OriginalL'auteur
Le problème que vous rencontrez dans votre
foreach
est que les structures sont des types valeur, et comme un résultat, la boucle variable d'itération n'est pas vraiment une référence à la structure dans la liste, mais plutôt une copie de la struct.Ma conjecture serait le compilateur est vous interdire de le changer parce qu'il n'aurait probablement pas faire ce que vous attendez de toute façon.
subItems.Clear()
est moins un problème, car bien que le champ peut être une copie de l'élément dans la liste, c'est aussi une référence à la liste (copie).La solution la plus simple serait probablement de changer à partir d'un
struct
à unclass
pour cela. Ou l'utilisation d'une approche totalement différente avec unfor (int ix = 0; ix < ...; ix++)
, etc.Les Classes sont des types référence... les classes peuvent être utilisées dans un foreach... donc oui... (pas sûr de ce qui a amené cette question) ... et désolé pas suffisamment de détails sur le "pour" l'approche... je ne dois pas avoir prêté assez d'attention à votre question. Je vois Fredrik déjà clarifié.
OriginalL'auteur
La boucle foreach ne fonctionne pas parce que
sInf
est une copie de la structure à l'intérieur des éléments. ChangersInf
ne changera pas le "réel" struct dans la liste.Clair fonctionne parce que vous n'êtes pas changer sInf, vous êtes à la modification de la liste à l'intérieur de sInf, et
Ilist<T>
sera toujours un type de référence.La même chose se produit lorsque vous utilisez l'opérateur d'indexation sur
IList<T>
- retourne une copie à la place de la struct. Si le compilateur ne permettentcatSlots[s].subItems = sortedSubTemp;
, vous serez en modifiant les points de la copie, et non les struct. Maintenant vous voyez pourquoi le compilateur indique la valeur de retour n'est pas une variable - la copie ne peut pas être référencée à nouveau.Il y a une solution simple sur la copie, puis écraser l'original struct avec votre copie.
Oui, il en résulte deux opérations de copie, mais c'est le prix à payer pour la valeur sémantique.
OriginalL'auteur
Les deux erreurs que vous avez spécifié avoir à faire avec le fait que vous êtes en utilisant les structures, ce qui en C# sont des types de valeur, pas de types de référence.
Vous ne pouvez absolument utiliser les types de référence dans les boucles foreach. Si vous changez les structures de classes, vous pouvez simplement faire ceci:
Avec des structs ce besoin de changement:
Les autres réponses ont de meilleures informations sur pourquoi les structures de travail différent que de classes, mais j'ai pensé que je pouvais l'aider avec le tri de la partie.
OriginalL'auteur
Si
subItems
a été changé pour une Liste concrète au lieu de l'interfaceIList
, alors vous seriez en mesure d'utiliser leSort
méthode.De sorte que votre totalité de la boucle devient:
Cela ne nécessite pas le contenu de la
struct
d'être modifiés à tout (généralement une bonne chose). Lestruct
's les membres seront toujours exactement les mêmes objets.Aussi, il y a très peu de bonnes raisons d'utiliser
struct
en C#. La GC est très bonne, très bonne, et vous seriez mieux avecclass
jusqu'à ce que vous avez démontré une allocation de mémoire goulot d'étranglement dans un profiler.Même de façon plus succincte, si
items
dansgAllCats[c].items
est aussi unList
, vous pouvez écrire:Edit: vous abandonner trop facilement! 🙂
Sort
est très facile à personnaliser. Par exemple:Que sortes, du plus jeune au plus vieux. (N'est-ce pas l'inférence de type bon en C# 3?)
OriginalL'auteur