Ne pouvez pas ajouter/supprimer des éléments d'une collection tout en foreach est une itération sur elle
Si je fais ma propre mise en œuvre de IEnumerator
l'interface, puis je suis capable ( à l'intérieur de foreach
déclaration )pour ajouter ou supprimer des éléments à partir d'un albumsList
sans générer une exception.Mais si foreach
instruction utilise IEnumerator
fourni par albumsList
, puis d'essayer d'ajouter/supprimer ( à l'intérieur de la boucle foreach )des éléments de albumsList
entraînera exception:
class Program
{
static void Main(string[] args)
{
string[] rockAlbums = { "rock", "roll", "rain dogs" };
ArrayList albumsList = new ArrayList(rockAlbums);
AlbumsCollection ac = new AlbumsCollection(albumsList);
foreach (string item in ac)
{
Console.WriteLine(item);
albumsList.Remove(item); //works
}
foreach (string item in albumsList)
{
albumsList.Remove(item); //exception
}
}
class MyEnumerator : IEnumerator
{
ArrayList table;
int _current = -1;
public Object Current
{
get
{
return table[_current];
}
}
public bool MoveNext()
{
if (_current + 1 < table.Count)
{
_current++;
return true;
}
else
return false;
}
public void Reset()
{
_current = -1;
}
public MyEnumerator(ArrayList albums)
{
this.table = albums;
}
}
class AlbumsCollection : IEnumerable
{
public ArrayList albums;
public IEnumerator GetEnumerator()
{
return new MyEnumerator(this.albums);
}
public AlbumsCollection(ArrayList albums)
{
this.albums = albums;
}
}
}
a) je suppose code qui lève une exception ( lors de l'utilisation de IEnumerator
mise en œuvre A
fourni par albumsList
) est situé à l'intérieur de A
?
b) Si je veux être en mesure d'ajouter ou de supprimer des éléments d'une collection ( tout en foreach
est une itération sur elle), ai-je toujours besoin de fournir mon propre mise en œuvre de IEnumerator
interface, ou peut albumsList être configuré pour autoriser l'ajout/suppression d'éléments?
merci
OriginalL'auteur flockofcode | 2010-06-14
Vous devez vous connecter pour publier un commentaire.
En général, il est déconseillé de la conception de la collection de classes qui vous permettent de modifier la collection lors de l'énumération, à moins que votre intention est de concevoir quelque chose de thread-safe plus spécifiquement, de sorte que c'est possible (par exemple, l'ajout d'un fil lors de l'énumération de l'autre).
Les raisons sont multiples. En voici une.
Votre
MyEnumerator
classe fonctionne en incrémentant un compteur interne. SonCurrent
propriété expose la valeur à l'index donné dans unArrayList
. Ce que cela signifie, c'est que l'énumération sur la collecte et la suppression de "chaque" élément de fait ne pas fonctionner comme prévu (c'est à dire, il ne supprimera pas tous les éléments de la liste).Envisager cette possibilité:
Le code que vous avez posté, sera effectivement le faire:
Current
de "la roche". Vous retirer "de la roche".["roll", "rain dogs"]
et vous incrément de votre index à 1, faireCurrent
égal à "rain dogs" (PAS de "roll"). Ensuite, vous supprimez "rain dogs."["roll"]
, et vous incrément de votre index à 2 (ce qui est >Count
); donc, votre agent recenseur pense que c'est fini.Il y a d'autres raisons, c'est une problématique de mise en œuvre. Par exemple quelqu'un à l'aide de votre code peut ne pas comprendre comment votre agent recenseur fonctionne (ni devrait ils -- la mise en œuvre devrait vraiment pas d'importance), et donc de ne pas réaliser que le coût de l'appel
Remove
dans unforeach
bloc encourt la peine deIndexOf
-- c'est à dire, un linéaire de recherche -- à chaque itération (voir la documentation MSDN surArrayList.Supprimer
pour vérifier cela).Fondamentalement, ce que je veux en venir c'est: vous n'avez pas voulez pour être en mesure de supprimer des éléments à l'intérieur d'un
foreach
boucle (de nouveau, sauf si vous êtes en train de concevoir quelque chose de thread-safe... peut-être).OK, alors, quelle est l'alternative? Voici quelques points pour vous aider à démarrer:
Clear
(pour supprimer tous éléments) ouRemoveAll
(pour supprimer les éléments correspondant à un filtre spécifié).ArrayList
a déjà unClear
méthode, comme le font la plupart de la collection de classes que vous pouvez utiliser dans le .NET. Sinon, si votre collection est indexé, une méthode commune pour supprimer plusieurs éléments à l'est par l'énumération à partir du haut de l'index à l'aide d'unfor
boucle et en appelantRemoveAt
sur des indices où la suppression est souhaitée (avis de cette mise à jour corrige deux problèmes à la fois: en allant vers l'arrière à partir du haut, vous assurer l'accès à chaque élément de la collection; en outre, en utilisantRemoveAt
au lieu deRemove
, vous éviter la peine de répété linéaire recherches).ArrayList
pour commencer. Aller avec fortement typé, générique homologues tels queList(Of Album)
à la place (en supposant que vous avez eu uneAlbum
classe -- dans le cas contraire,List(Of String)
est encore plus typesafe queArrayList
).Juste par curiosité: est - il de l'agent recenseur fournis par albumsList qui fait détecte ( et, par conséquent, déclenche une exception ) que nous essayons de supprimer/ajouter des éléments? De toute façon, suivra vos directives
Oui et non. En réalité, la façon dont
ArrayList.GetEnunerator
œuvres est par la création d'un objet avec une référence à la sous-jacentesArrayList
objet, qui gère un certain nombre de représenter son état. LorsqueMoveNext
est appelé l'agent recenseur, il vérifie ce nombre à l'encontre de ce qu'il était lorsque l'agent recenseur a été construit et lève une exception si les numéros ne correspondent pas. C'est donc un échange entre lesArrayList
et l'énumérateur que les résultats de l'exception levée.merci à vous tous pour votre aide
C'est dommage que Microsoft n'a pas définir les variations si IEnumerable, avec un traitement différent de la collection-modifié le scénario. Au moins deux j'aurais aimé aurait été IMultipassEnumerable, dont l'agent recenseur comprendrait Reset et le Comte de méthodes et de garantir que plusieurs passages pour obtenir les mêmes éléments, IModifiableEnumerable, qui permettrait la modification au cours de l'énumération (mais pas nécessairement thread-safe, et avec aucune garantie que lorsque des modifications pourraient être observés, sans briser l'agent recenseur, et ThreadSafeModifiableEnumerator.
OriginalL'auteur Dan Tao
Façon la plus simple est d'inverser les éléments comme
for(int i = items.Count-1; i >=0; i--)
, ou boucle une fois, de recueillir tous les éléments à supprimer dans la liste, puis une boucle sur les éléments à supprimer, en les supprimant de la liste d'origine.Même raison que j'ai commencer à partir de l'arrière lors de la recherche et de la modification de n'importe quel type de texte: la position de l'élément "suivant" ne change pas. 🙂
Peut-être, for(int i = éléments.Count - 1; ... devraient être mieux?
bonne prise! fixe.
OriginalL'auteur Steve Cooper
Supposons que j'ai une collection, un tableau pour que la matière
J'ai une fonction
Maintenant j'ai appeler cette fonction et itérer sur et essayez d'ajouter
Sera la cause d'une exception d'exécution
Ici, chose à remarquer, c'est que si nous sommes autorisés à ajouter pour chaque élément
dans la liste
liste va devenir quelque chose comme
{1,2,3,4,5,6}
ensuite, pour chaque élément et chaque nouvellement ajouté, à nous maintenir sur l'ajout de coz de
nous allons être coincé dans un infini de l'opération qu'il va de nouveau être répétée pour chaque élément
OriginalL'auteur GPuri
De la documentation MSDN pour INotifyCollectionChanged:
Me semble que le problème est dans la Collection elle-même, et non son agent Recenseur.
Oui. Si la Collection que vous avez à parcourir n'a pas mis en œuvre INotifyCollectionChanged, puis de tenter de modifier dans un foreach bloc lèvera une exception. Cela explique la cause du problème, et des points à la façon de le résoudre.
Craig Trader: je pense que LukeH la question vient du fait qu'il n'y a pas de preuve claire que l'OP se pose au sujet de l'interaction avec une INTERFACE utilisateur. En outre, la mise en œuvre de
INotifyCollectionChanged
n'est pas la baguette magique pour vous permettre d'ajouter/supprimer à partir de l'intérieur d'unforeach
bloc."Oui. Si la Collection que vous avez à parcourir n'a pas mis en œuvre INotifyCollectionChanged, puis de tenter de modifier dans un foreach bloc lèvera une exception" Mais la collecte AlbumsCollection ne pas mettre en œuvre INotifyCollectionChanged, et encore foreach bloc ne lance pas d'exception
"Je pense que LukeH la question vient du fait qu'il n'y a pas de preuve claire que l'OP se pose au sujet de l'interaction avec une INTERFACE utilisateur." J'ai récemment commencé à apprendre la programmation, donc je n'ai pas encore couverts toute l'INTERFACE utilisateur de la technologie (comme Asp.Net)
OriginalL'auteur Craig Trader