2010-08-23 23 views
9

¿Por qué un bucle foreach es de solo lectura? ¿Qué razones hay para esto?¿Por qué no puedo modificar la variable de bucle en un foreach?

+1

No estoy seguro de lo que quiere decir con un bucle de solo lectura. ¿Se refiere al hecho de que no debe modificar la variable de iteración del ciclo? Es decir, para cada elemento en myitems item.property = algo siguiente – Tommy

+0

No sé cómo aplicar el concepto de solo lectura a un for-loop. ¿O te refieres a por qué dicen que es de solo lectura? – helios

+2

Si estás hablando de la enumeración, ¡sí! No puede cambiar la enumeración por la que está pasando. –

Respuesta

22

No estoy seguro exactamente lo que entendemos por un "bucle de sólo lectura", pero supongo que desea saber por qué esto no se compila:

int[] ints = { 1, 2, 3 }; 
foreach (int x in ints) 
{ 
    x = 4; 
} 

El código anterior dará la siguiente compile error:

 
Cannot assign to 'x' because it is a 'foreach iteration variable' 

¿Por qué esto no se permite? Intentar asignarlo probablemente no haría lo que desea, no modificaría el contenido de la colección original. Esto se debe a que la variable x no es una referencia a los elementos en la lista, es una copia. Para evitar que la gente escriba código defectuoso, el compilador no permite esto.

+0

+1. Es similar a decir 'Foo x = new Foo(); Foo y = x; y = new Foo(); 'Configurando' y' a 'new Foo()' no hace nada con 'x'. Del mismo modo, tener 'foo' como su variable de bucle y decir' foo = new Foo(); '(si pudiera **) no le hace nada al' Foo' en la colección. –

+1

Es decir (la parte de copia) no es verdadera para el tipo de referencia, entonces x mantendría una referencia al objeto en IEnumerable –

+0

@Rune FS: en el caso de los tipos de referencia, es una * copia * de la referencia. Incluso en este caso, probablemente no haría lo que usted quería si pudiera asignarlo. –

5

Supongo que es así como el iterador viaja a través de la lista.

Digamos que usted tiene una lista ordenada:

Alaska 
Nebraska 
Ohio 

En medio de

foreach(var s in States) 
{ 
} 

Haces un States.Add ("Missouri")

¿cómo manejar eso? ¿Entonces saltas a Missouri incluso si ya has superado ese índice?

+0

El contrato de Microsoft para iEnumerable dice que un iEnumerable no debe proporcionar ningún medio para actualizar la colección sin causar una excepción cuando enumerando el siguiente elemento. Si tuviera mi druthers, un enumerador en su escenario sería libre de incluir o no incluir a Mississippi, pero se le requeriría enumerar exactamente una vez todos los elementos que existían a lo largo de la enumeración, o lanzar una excepción si no pudiera hacerlo. – supercat

3

Si, por esto, decir:

Why shouldn't I modify the collection that's being foreach 'd over?

No hay ninguna garantía de que los artículos que usted está recibiendo la luz en un orden dado, y que la adición de un elemento, o la eliminación de un elemento no lo hará hace que el orden de los elementos en la colección cambie, o incluso que el Enumerador deje de ser válido.

Imagínese si ejecutó el siguiente código:

var items = GetListOfTOfSomething(); // Returns 10 items 

int i = 0; 
foreach(vat item in items) 
{ 
    i++; 
    if (i == 5) 
    { 
     items.Remove(item); 
    } 
} 

Tan pronto como se golpeó el bucle donde i es 6 (es decir, después de retirar el artículo) cualquier cosa podría suceder. Es posible que el Enumerador se haya invalidado debido a que quita un elemento, todo podría haberse "mezclado por uno" en la colección subyacente, lo que haría que un elemento ocupara el lugar del elemento eliminado, lo que significa que "omitirá" uno.

Si quería decir "por qué no puedo cambiar el valor que se proporciona en cada iteración", entonces, si la colección está trabajando con value types contiene, cualquier cambio que realice no se conservan, ya que es una valor con el que está trabajando, en lugar de una referencia .

0

foreach funciona con todo lo que implementa la interfaz IEnumerable. Para evitar problemas de sincronización, el enumerable nunca se modificará mientras se itera sobre él.

Los problemas surgen si agrega o quita elementos en otro hilo mientras itera: dependiendo de dónde se encuentre puede perder un elemento o aplicar su código a un elemento adicional. Esto es detectado por el tiempo de ejecución (¿en algunos casos o en todos?) Y produce una excepción:

System.InvalidOperationException was unhandled 
    Message="Collection was modified; enumeration operation may not execute." 

foreach intenta siguiente punto en cada iteración que puede causar problemas si se está modificando desde otro hilo al mismo tiempo.

1

El comando foreach utiliza la interfaz IEnumerable para recorrer la colección. La interfaz solo define métodos para recorrer una colección y obtener el elemento actual, no hay métodos para actualizar la colección.

Como la interfaz solo define los métodos mínimos necesarios para leer el conjunto en una dirección, la interfaz puede implementarse mediante una amplia gama de colecciones.

Como solo tiene acceso a un solo elemento a la vez, no es necesario que toda la colección exista al mismo tiempo. Esto es, por ejemplo, utilizado por las expresiones LINQ, donde crea el resultado sobre la marcha a medida que lo lee, en lugar de crear primero el resultado completo y luego permitirle recorrerlo.

1

No estoy seguro de lo que quiere decir con solo lectura, pero supongo que comprender lo que el bucle foreach está bajo el capó ayudará. Es el azúcar sintáctica y también se podría escribir algo como esto:

IEnumerator enumerator = list.GetEnumerator(); 
while(enumerator.MoveNext()) 
{ 
    T element = enumerator.Current; 
    //body goes here 
} 

Si cambia la colección (lista) se está haciendo difícil imposible encontrar la manera de procesar la iteración. Asignando al elemento (en la versión foreach) se podría ver como intentar asignar al enumerador. Actual que es de solo lectura o que intenta cambiar el valor del local que contiene una referencia al enumerador. En este momento, también podría introducir un local usted mismo porque ya no tiene nada que ver con la lista enumerada.

Cuestiones relacionadas