Pregunta

Tengo una pregunta que me sorprende que ya no se haya hecho exactamente en este formato.

Si tengo un IEnumerable que se genera en función de la iteración a través de una fuente de datos, (y usando una declaración de devolución de rendimiento), ¿cómo puedo detectar cuándo ha habido una modificación a la fuente después de un acceso a través de un enumerador que se generó a través de un ¿Llamada de getenumerator?

Aquí está la parte extraña: no estoy multiproteo. Creo que mi pregunta tiene un defecto en algún lugar, porque esto debería ser simple. . . Solo quiero saber cuándo ha cambiado la fuente y el iterador está desactualizado.

Muchas gracias.

¿Fue útil?

Solución

Debería manejar la creación del enumerador usted mismo para rastrear esta información o, en un uso mínimo. yield return; con su propio tipo de seguimiento de modificación en su lugar.

La mayoría de las clases de recopilación del marco, por ejemplo, mantienen un número de "versión". Cuando hacen un enumerador, mantienen una instantánea de ese número de versión y lo verifican durante MoveNext(). Podrías hacer el mismo cheque antes de llamar yield return XXX;

Otros consejos

La mayoría de las clases de recopilación en .NET BCL utiliza un atributo de versión para el seguimiento de cambios. Es decir: el enumerador se construye con un número de versión (entero) y verifica que la fuente original del número de versión sigue siendo la misma iteración (cuando se llama a MoveExt). La colección a su vez incrementa el atributo de versión cada vez que se realiza una modificación. Este mecanismo de seguimiento es simple y efectivo.

Otras 2 formas que he visto son:

Hacer que la colección tenga una colección interna que contenga referencias débiles a enumeradores sobresalientes. Y cada vez que se hace una modificación a la colección, hace que cada enumerador que todavía esté vivo no sea válido.

O implementar eventos en la colección (inotifyCollectionChanged) y simplemente registrarse en ese evento en el enumerador. Y si se levanta, marque el enumerador como inválido. Este método es relativamente fácil de implementar, genérico y viene sin mucha gastos generales, pero requiere su colección para admitir eventos

Microsoft sugiere cualquier modificación a una colección IEnumerable en caso de anular cualquier objeto IEnumerator existente, pero esa política rara vez es particularmente útil y, a veces, puede ser una molestia. No hay ninguna razón por la cual el autor de un IEnumerable/IEnumerator debería sentir la necesidad de lanzar una excepción si una colección se modifica de una manera que no impida que el IEnumerator devuelva los mismos datos que hubiera regresado sin dicha modificación. Volvería más allá y sugeriría que debería considerarse deseable, cuando sea posible, que un enumerador permanezca funcional si puede obedecer las siguientes restricciones:

  1. Los elementos que están en la colección durante la duración de la enumeración deben devolverse exactamente una vez.
  2. Cada elemento que se agrega o elimina durante la enumeración puede devolverse cero o una vez, pero no más de uno. Si se elimina un objeto de la colección y se vuelve a agregar, se puede considerar originalmente alojado en un elemento, pero se pone en uno nuevo, por lo que la enumeración puede devolver legítimamente la anterior, la nueva, ambos o ninguno.

La clase VisualBasic.Collection se comporta de acuerdo con las restricciones anteriores; Tal comportamiento puede ser muy útil, lo que permite enumerar a través de la clase y eliminar elementos que cumplen con un criterio particular.

Por supuesto, diseñar una colección para comportarse con sensatez si se modifica durante la enumeración puede no ser necesariamente más fácil que lanzar una excepción, pero para colecciones de tamaño razonable se puede obtener dicha semántica haciendo que el enumerador convierta la colección en una lista y enumere el contenido de la lista. Si lo desea, y especialmente si no se requiere seguridad del hilo, puede ser útil que la colección mantenga una referencia fuerte o débil a la lista devuelta por su enumerador, y anule dicha referencia en cualquier momento que se modifique. Otra opción sería tener una referencia "real" a la colección que se mantenga en una clase de envoltorio, y que la clase interna mantenga un recuento de cuántos enumeradores existen (los enumeradores obtendrían una referencia a la colección real). Si se intenta modificar la colección mientras existen los enumeradores, reemplace la instancia de la colección con una copia y luego haga las modificaciones sobre eso (la copia comenzaría con un recuento de referencia de cero). Tal diseño evitaría hacer copias redundantes de la lista, excepto en el escenario donde se abandona un Ienumerator sin ser eliminado; Incluso en ese escenario, a diferencia de los escenarios que involucran referencias o eventos débiles, no se mantendrían vivos objetos vivos de lo necesario.

No he encontrado una respuesta, pero como trabajo acabo de ver la excepción como esta (ejemplo de 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;
                }
            }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top