2011-02-23 13 views
9

Tengo el código que está siendo ejecutado cada fotograma de un juego:C# - Modificar el valor del diccionario par clave-valor, mientras que en foreach

foreach (var repeaterAction in conditionTimes.Keys) 
    { 
     if (repeaterAction.Condition() == true) 
     { 
      if (conditionTimes[repeaterAction] == TimeSpan.Zero) 
      { 
       repeaterAction.Action(); 
      } 
      else if (conditionTimes[repeaterAction] >= repeaterAction.InitialLapse) 
      { 
       repeaterAction.Action(); 
       conditionTimes[repeaterAction] -= repeaterAction.ActionInterval; 
      } 
      conditionTimes[repeaterAction] += gameTime.ElapsedGameTime; 
     } 
     else 
     { 
      conditionTimes[repeaterAction] = TimeSpan.Zero; 
     } 
    } 

Esto me da el siguiente error:

Collection was modified; enumeration operation may not execute.

¿Hay alguna forma de modificar el valor en el par clave-valor dentro del bucle foreach, sin copiar el diccionario en cada cuadro?

Respuesta

0

Lo sentimos, no puede.

Supongo que su mejor opción es crear un nuevo diccionario y luego cambiarlo por el anterior cuando haya terminado con el ciclo foreach.

0

debe usar otro patrón para hacer lo que está intentando hacer porque el para cada uno no le permite cambiar el enumerador al que está haciendo un bucle. Imagina si ejecutas un foreach en una lista ordenada desde el principio, comienzas a procesar el elemento con la tecla = "A" luego vas a "B" y luego cambias "C" a "B", ¿qué va a pasar? Se recurre a su lista y ya no sabe qué es lo que está bucleando y dónde se encuentra.

en general, "podría" hacerlo con un for (int i = dictionary.count-1; i> = 0; --i) o algo así, pero esto también depende de su contexto, realmente lo intentaría para usar otro enfoque.

0

No puede modificar una colección al enumerarla dentro de una declaración foreach. Así que tendrías que copiarlo cada vez (solo necesitas copiar las teclas). Otra opción sería almacenar una lista de claves separada en todo momento (por ejemplo, encapsulando el diccionario dentro de una clase que maneja esto). algo así como:

class MyDictionary<TKey, TValue> 
{ 
    private Dictionary<TKey, TValue> _dict = new Dictionary<TKey, TValue>(); 
    private List<Keys> _keys = new List<TKey>(); 

    public void Add(TKey key, TValue value) 
    { 
     _dict.Add(key, value); 
     _keys.Add(key); 
    } 

    //public bool Remove ... 
    //indexer... 
} 

Por supuesto, en un entorno concurrente tendría que asegurarse de que la lista de diccionario y se sincronizan ...

0

Un enfoque común es recordar las teclas que desee cambiar en el primer ciclo, y luego tiene un segundo que recorre las teclas recordadas y cambia el diccionario original. Eso evita crear un diccionario completamente nuevo con todos los elementos.

3

No, no puedes. Hay una opción desagradable que se podría utilizar:

public class Wrapper<T> 
{ 
    public T WrappedValue { get; set; } 

    // *Maybe* add implicit conversions here? Icky... 
} 

Entonces será crear (por ejemplo) un Dictionary<string, WrappedValue<int>> ... iterar sobre los pares clave/valor, y cambiar el valor dentro de la envoltura en lugar de hacer que la entrada en sí se refiera a un envoltorio diferente.

Creo que no recomendaría esto sin embargo, sería incómodo de usar, y fácil de mal uso.

Otra opción si está usando .NET 4 es usar ConcurrentDictionary, que hace permitir modificaciones concurrentes.

13

Estoy desaconsejando tratar de modificar una colección mientras navego por ella con diccionarios, sin embargo es posible porque el acceso directo a las teclas está disponible.Sólo tiene que añadir .ToArray() después de la conditionTimes.Keys en los foreach luego las teclas se convierte en una recogida selectiva y se puede modificar el diccionario:

foreach (var repeaterAction in conditionTimes.Keys.ToArray()) 
{ 
    if (repeaterAction.Condition() == true) 
    { 
     if (conditionTimes[repeaterAction] == TimeSpan.Zero) 
     { 
      repeaterAction.Action(); 
     } 
     else if (conditionTimes[repeaterAction] >= repeaterAction.InitialLapse) 
     { 
      repeaterAction.Action(); 
      conditionTimes[repeaterAction] -= repeaterAction.ActionInterval; 
     } 
     conditionTimes[repeaterAction] += gameTime.ElapsedGameTime; 
    } 
    else 
    { 
     conditionTimes[repeaterAction] = TimeSpan.Zero; 
    } 
} 

También tiene que modificar estás código de modo que si los cambios clave que realmente eliminar una entrada de su diccionario y agregue el nuevo, porque las claves realmente no se pueden cambiar, simplemente se eliminan.
De nuevo, esto no es recomendable.

+0

¡Esto es asombroso! Gracias has hecho mi día – zabulus

Cuestiones relacionadas