Domanda

Ho una domanda che sono sorpreso che non sia già stata posta esattamente in questo formato.

Se ho un IEnumerable che viene generato in base all'iterazione tramite una fonte di dati (e usando un'istruzione di reso del rendimento), come posso rilevare quando c'è stata una modifica alla fonte dopo un accesso tramite un enumeratore che è stato generato tramite a Chiamata getEnumerator?

Ecco la parte strana: non sono multi-threading. Penso che la mia domanda abbia un difetto da qualche parte, perché dovrebbe essere semplice. . . Voglio solo sapere quando è cambiata la fonte e l'iteratore è obsoleto.

Grazie molte.

È stato utile?

Soluzione

Dovresti gestire la creazione dell'enumeratore tu stesso per tracciare queste informazioni o, ad un uso minimo yield return; con il tuo tipo di monitoraggio di modifica in atto.

La maggior parte delle classi di raccolta Framework, ad esempio, mantengono un numero di "versione". Quando fanno un enumeratore, mantengono un'istantanea di quel numero di versione e lo controllano durante MoveNext(). Potresti fare lo stesso assegno prima di chiamare yield return XXX;

Altri suggerimenti

La maggior parte delle classi di raccolta in .NET BCL utilizza un attributo versione per il monitoraggio della modifica. Cioè: l'enumeratore è costruito con un numero di versione (intero) e controlla che la fonte originale del numero di versione è sempre la stessa ogni iterazione (quando viene chiamato movenext). La raccolta a sua volta aumenta l'attributo della versione ogni volta che viene apportata una modifica. Questo meccanismo di tracciamento è semplice ed efficace.

Altri 2 modi in cui ho visto sono:

Avere la raccolta contiene una raccolta interna contenente riferimenti deboli a enumeratori eccezionali. E ogni volta che viene apportata una modifica alla collezione, rende ogni enumeratore che è ancora vivo non valido.

O implementazione di eventi nella raccolta (inotifyCollectionChanged) e registrarsi semplicemente su quell'evento nell'enumeratore. E se sollevato, contrassegnare l'enumeratore come non valido. Questo metodo è relativamente facile da implementare, generico e viene senza tanti sovraccarichi, ma richiede la tua raccolta per supportare eventi

Microsoft suggerisce che qualsiasi modifica a una collezione ienumerable dovrebbe annullare qualsiasi oggetto Ienumerator esistente, ma tale politica è raramente particolarmente utile e a volte può essere un fastidio. Non vi è alcun motivo per cui l'autore di un Ienumerable/Ienumerator dovrebbe sentire la necessità di lanciare un'eccezione se una raccolta viene modificata in modo da non impedire all'Ienumeratore di restituire gli stessi dati che sarebbe tornata senza tale modifica. Vorrei andare oltre e suggerire che dovrebbe essere considerato desiderabile, quando possibile, avere un enumeratore che rimanga funzionale se può obbedire ai seguenti vincoli:

  1. Gli articoli che sono nella raccolta per tutta la durata dell'enumerazione devono essere restituiti esattamente una volta.
  2. Ogni elemento che viene aggiunto o eliminato durante l'enumerazione può essere restituito zero o una volta, ma non più di uno. Se un oggetto viene rimosso dalla raccolta e ri-aggiunto, può essere considerato originariamente ospitato in un oggetto ma messo in uno nuovo, quindi l'enumerazione può restituire legittimamente quella vecchia, quella nuova, entrambi o nessuno dei due.

La classe VisualBasic.Collection si comporta in base ai vincoli di cui sopra; Tale comportamento può essere molto utile, consentendo di elencare attraverso la classe e rimuovere gli articoli che soddisfano un criterio particolare.

Naturalmente, la progettazione di una collezione per comportarsi in modo ragionevole se viene modificata durante l'enumerazione potrebbe non essere necessariamente più facile che lanciare un'eccezione, ma per raccolte di dimensioni ragionevoli tale semantica può essere ottenuta facendo convertire l'enumeratore in un elenco e elencare il contenuto di la lista. Se lo si desidera, e soprattutto se non è necessaria la sicurezza del thread, può essere utile che la raccolta mantenga un riferimento forte o debole all'elenco restituito dal suo enumeratore e annulla tale riferimento ogni volta che viene modificata. Un'altra opzione sarebbe quella di avere un riferimento "reale" alla collezione in una classe wrapper e far sì che la classe interna mantenga un conteggio di quanti enumeratori esistono (gli enumeratori otterrebbero un riferimento alla raccolta reale). Se viene fatto un tentativo di modificare la raccolta mentre esistono enumeratori, sostituire l'istanza di raccolta con una copia e quindi apportare le modifiche su quella (la copia inizierebbe con un conteggio di riferimento di zero). Tale design eviterebbe di realizzare copie ridondanti dell'elenco tranne nello scenario in cui un ienumeratore viene abbandonato senza essere smaltito; Anche in quello scenario, a differenza degli scenari che coinvolgono referenze o eventi deboli, nessun oggetto sarebbe mantenuto in vita più del necessario.

Non ho trovato una risposta, ma come lavoro intorno ho appena preso l'eccezione come questa (esempio 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;
                }
            }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top