La Collection a été modifiée; opération d'énumération peut pas exécuter - pourquoi?
Je suis énumération sur une collection qui met en œuvre IList, et lors de l'énumération, je suis la modification de la collecte. Je reçois le message d'erreur, "la Collection a été modifiée; opération d'énumération peut pas s'exécuter."
Je veux savoir pourquoi cette erreur se produit lors de la modification d'un élément de la collection au cours d'une itération. J'ai déjà converti ma boucle foreach pour une boucle for, mais je veux connaître les détails sur les raisons de cette erreur se produit.
OriginalL'auteur contactmatt | 2010-12-27
Vous devez vous connecter pour publier un commentaire.
De la IEnumerable documentation:
Je crois que la raison de cette décision est qu'il ne peut être garanti que tous les types de collections peut maintenir modification et de préserver un énumérateur de l'état. Considérons une liste liée -- si vous supprimer un nœud et d'un agent recenseur est actuellement sur ce nœud, le nœud de référence peut être son seul état. Et une fois que le nœud est supprimé, le "nœud" de référence sera définie à
null
, effectivement invalider l'énumérateur de l'état et de prévenir d'autres énumération.Depuis quelques collecte des implémentations aurait de graves ennuis avec ce genre de situation, il a été décidé de faire de cette partie de l'interface IEnumerable contrat. Permettant de modifier dans certaines situations et pas dans d'autres serait horriblement confus. En outre, cela signifierait que le code existant qui pourrait s'appuyer sur la modification d'une collection lors de l'énumération, il aurait de sérieux problèmes lors de la mise en œuvre de la collection est changé. Afin de rendre le comportement uniforme dans toutes les enumerables est préférable.
OriginalL'auteur cdhowie
Comment
En interne, la plupart de la collection de base de classes de conserver un numéro de version. Chaque fois que vous ajouter, supprimer, réorganiser, etc la collection, ce numéro de version est incrémenté.
Lorsque vous démarrez l'énumération, un instantané de la numéro de version est pris. Chaque fois autour de la boucle, ce numéro de version est comparée à la collection, et s'ils sont différents, cette exception est levée.
Pourquoi
Alors qu'il serait possible de mettre en œuvre
IList
afin qu'il puisse traiter correctement avec les modifications apportées à la collecte faite dans la boucle foreach (en ayant l'énumérateur de la piste à la collection des changements), il est beaucoup plus difficile la tâche de traiter correctement avec les modifications apportées à la collecte par les autres threads lors de l'énumération. Si cette exception existe pour aider à identifier les vulnérabilités de votre code, et de fournir une certaine forme d'alerte précoce sur les éventuels instabilité provoquée par la manipulation des autres threads.VisualBasic.Collection
type ne se traiter avec succès avec les changements qui se produisent au cours de l'énumération, et peut-être la premièreIEnumerator
type de mettre en œuvreIDisposable
(abandonné les agents recenseurs seront nettoyés par un GC, mais peut-être un rôle important dans la performance de vidange jusqu'alors). Je souhaite que la documentation deIEnumerable
avait précisé qu'il faut jeter l'exception de moins, il est équipé pour continuer l'énumération sensiblement, et avait précisé ce qui promet un "sensible" de l'énumération doit répondre.OriginalL'auteur Drew Noakes
Tandis que d'autres ont décrit pourquoi il est invalide comportement, ils n'ont pas offert une solution à votre problème. Si vous ne pouvez pas besoin d'une solution, je vais donner de toute façon.
Si vous voulez observer une collection qui est différent de la collection que vous êtes à parcourir, vous devez retourner une nouvelle collection.
Par exemple..
C'est la façon dont vous devriez être "modification" des collections. LINQ prend cette approche est utile lorsque vous souhaitez modifier une séquence. Par exemple:
sequence.Where()
renvoie une nouvelle séquence. Il renvoie plutôt à un wrapper énumérables qui va énumérersequence
et retourner uniquement les éléments qui correspondent le prédicat. Il retourne un objet qui produit probablement une séquence différente lorsque énumérés, mais il n'est pas entièrement nouvelle séquence. Si vous deviez modifiersequence
(si c'était mutable), vous devriez voir ces changements prochaine fois que vous avez énumérénewSequence
.OriginalL'auteur Josh Smeaton
Je suppose que la raison en est que l'état de l'objet énumérateur est liée à l'état de la collection. Par exemple, une liste d'énumérateur aurait un champ int pour stocker l'index de l'élément courant. Toutefois, si vous supprimez un élément de la liste que vous passer tous les indices après l'élément à gauche par un. À ce stade, l'agent recenseur serait ignorer un objet ainsi manifestant des comportements erronés. Faire de l'agent recenseur valable pour tous les cas possibles nécessiterait une logique complexe et peut nuire à la performance pour le cas le plus courant (pas de changement de la collection). Je crois que c'est pourquoi les concepteurs de la collections .NET a décidé qu'ils devraient tout simplement jeter une exception lorsque l'agent recenseur est dans invald de l'état au lieu d'essayer de le réparer.
OriginalL'auteur Stilgar
La vraie raison technique dans ce scénario, c'est parce que les Listes contiennent un membre privé appelé "version". Chaque modification - Ajout/suppression - incrémente le numéro de Version. L'agent Recenseur qui GetEnumerator renvoie les magasins de la version au moment où il est créé et vérifie la Version à chaque fois Suivant est appelé - si ce n'est pas l'égalité, elle lève une exception.
Cela est vrai pour la builtin
List<T>
classe et, éventuellement, pour d'autres collections, donc si vous implémentez votre propre IList (plutôt que de simplement sous-classement/à l'aide d'une construite en la collection en interne), alors vous pouvez être en mesure de travailler autour de cela, mais en général, l'énumération et la mofication doit être fait dans un en arrière de la boucle ou à l'aide d'une Liste secondaire, selon le scénario.Noter que la modification d'un élément est parfaitement bien, seulement Ajouter/Supprimer n'est pas.
OriginalL'auteur Michael Stum
C'est le spécifié comportement:
De cette limitation, les agents recenseurs plus simple à mettre en œuvre.
Notez que certaines collections (à tort) vous permettent d'énumérer tout en modifiant la collection.
OriginalL'auteur SLaks
La raison pour laquelle vous voyez que c'est Le IEnumerator renvoyé par la collection sous-jacente peut exposer l'actuelle propriété en lecture seule. En général, vous devriez éviter les changements de collections(en fait la plupart des cas, vous n'aurez même pas être en mesure de modifier la collection) à l'aide de for-each.
OriginalL'auteur THE DOCTOR