Question

J'ai une question que je me suis surpris n'a pas déjà été posée exactement ce format.

Si j'ai un IEnumerable qui est généré en fonction de l'itération à partir d'une source de données, (et à l'aide d'une instruction yield return), comment puis-je détecter quand il y a eu une modification de la source après avoir accès par l'intermédiaire d'un agent Recenseur qui a été généré via un GetEnumerator appel?

Ici est la partie étrange:Je ne suis pas le multi-threading.Je pense que ma question a un défaut quelque part, parce que cela devrait être simple...Je veux juste savoir quand la source a changé et l'itérateur est pas à jour.

Je vous remercie beaucoup.

Était-ce utile?

La solution

Vous devez gérer la création de l'agent recenseur vous-même pour le suivi de cette information, ou, au minimum, l'utilisation yield return; avec votre propre type de modification de suivi en place.

La plupart des le cadre des classes de collection, par exemple, de garder une "version" le nombre.Quand ils font un agent recenseur, ils gardent un instantané de ce numéro de version, et le vérifier au cours de MoveNext().Vous pourriez faire la même vérifier avant d'appeler yield return XXX;

Autres conseils

La plupart des classes de collection dans le .NET BCL utilisent un attribut de version pour le suivi des modifications.C'est-à-dire que l'énumérateur est construit avec un numéro de version (entier) et vérifie que la source d'origine du numéro de version est toujours la même chaque itération (lorsque MOVENEXT est appelé).La collection augmente à tour l'attribut de version à chaque fois qu'une modification est faite.Ce mécanisme de suivi est simple et efficace.

2 autres façons que j'ai vues sont:

Avoir la collection détenir une collection interne contenant des références faibles aux énumérateurs exceptionnels.Et chaque fois qu'une modification est faite à la collection, elle rend chaque énumérateur qui est toujours valide non valide.

ou des événements de mise en œuvre de la collection (inotifyCollectionChanged) et simplement inscrivez-vous sur cet événement dans l'énumérateur.Et si soulevé, marquez l'énumérateur comme invalide.Cette méthode est relativement facile à mettre en œuvre, générique et ne se déroule pas sans trop de surcharge mais nécessite votre collecte pour prendre en charge les événements

Microsoft suggère que toute modification d'une collection iEnumerable devrait annuler les objets d'ienumerator existants, mais cette politique est rarement particulièrement utile et peut parfois être une nuisance. Il n'y a aucune raison pour que l'auteur d'un iénumérateur / ienumerator devrait ressentir une nécessité de jeter une exception si une collection est modifiée d'une manière qui n'empêchera pas l'ienumerator de renvoyer les mêmes données qu'il leur serait retourné sans cette modification. J'irais plus loin et suggérez qu'il soit considéré comme souhaitable, lorsque cela est possible, d'avoir un énumérateur restent fonctionnel s'il peut obéir aux contraintes suivantes:

  1. Les articles qui sont dans la collection pendant toute la durée de l'énumération doivent être retournés exactement une fois.
  2. Chaque élément ajouté ou supprimé pendant l'énumération peut être renvoyé de zéro ou une fois, mais pas plus d'un. Si un objet est retiré de la collecte et de re-ajouté, il peut être considéré comme ayant été installé à l'origine dans un article mais mis en une nouvelle, de sorte que l'énumération peut légitimement retourner l'ancien, le nouveau, à la fois ou non.

    La classe VisualBasic.collection se comporte conformément aux contraintes ci-dessus; Ce comportement peut être très utile, permettant d'énumérer à travers la classe et de supprimer des éléments répondant à un critère particulier.

    Bien entendu, concevez une collection pour se comporter de manière raisonnable si elle est modifiée lors de l'énumération ne peut pas nécessairement être plus facile que de jeter une exception, mais pour des collections de taille raisonnable, une telle sémantique peut être obtenue par l'énumérateur convertissant la collection à une liste et énumérer le contenu de la liste. Si vous le souhaitez, et surtout si la sécurité du thread n'est pas requise, il peut être utile de disposer de la collection de conserver une référence forte ou faible à la liste renvoyée par son énumérateur et annuler une telle référence à tout moment. Une autre option serait d'avoir une référence «réelle» à la collection se dérouler dans une classe d'emballage et que la classe intérieure conserve un nombre de compensateurs de l'existence (énumérateurs obtiendrait une référence à la vraie collection). Si une tentative est faite de modifier la collection pendant que des énumérateurs existent, remplacez l'instance de collecte avec une copie, puis apportez les modifications à ce sujet (la copie commencerait par un nombre de référence de zéro). Une telle conception éviterait de faire des copies redondantes de la liste, sauf dans le scénario où un ienumerator est abandonné sans être déposé; Même dans ce scénario, contrairement aux scénarios impliquant des faiblesses ou des événements, aucun objet ne serait maintenu en vie plus longtemps que nécessaire.

Je n'ai pas trouvé de réponse, mais en tant que travail autour, je viens de saisir l'exception comme celle-ci (exemple WPF):

            while (slideShowOn)
            {

                if (this.Model.Images.Count < 1)
                {
                    break;
                }

                var _bitmapsEnumerator = this.Model.Images.GetEnumerator();

                try
                {
                    while (_bitmapsEnumerator.MoveNext())
                    {
                        this.Model.SelectedImage = _bitmapsEnumerator.Current;


                        Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
                        Thread.Sleep(41);
                    }
                }
                catch (System.InvalidOperationException ex)
                {
// Scratch this bit: the error message isn't restricted to English
//                     if (ex.Message == "Collection was modified; enumeration operation may not execute.")
//                        {
//
//                        }
//                        else throw ex;
                }
            }

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