loop for array c# foreach iteration accessor

c# - array - ¿Por qué no podemos asignar una variable de iteración foreach, mientras que podemos modificarla completamente con un descriptor de acceso?



foreach c# list (9)

Solo tenía curiosidad sobre esto: el siguiente código no compilará, porque no podemos modificar una variable de iteración foreach:

foreach (var item in MyObjectList) { item = Value; }

Pero lo siguiente se compilará y ejecutará:

foreach (var item in MyObjectList) { item.Value = Value; }

¿Por qué es el primer inválido, mientras que el segundo puede hacer lo mismo debajo? (Estaba buscando la expresión correcta en inglés para esto, pero no lo recuerdo. ¿Bajo el ...? ^^)


El punto es que no se puede modificar la colección en sí mientras se itera sobre ella. Es absolutamente legal y común modificar los objetos que produce el iterador.


El segundo no está haciendo lo mismo en absoluto. No está cambiando el valor de la variable del item : está cambiando una propiedad del objeto al que se refiere ese valor. Estos dos solo serían equivalentes si el item es un tipo de valor mutable, en cuyo caso debe cambiarlo de todos modos, ya que los tipos de valores mutables son malos. (Se comportan de muchas maneras que el desarrollador desprevenido puede no esperar).

Es lo mismo que esto:

private readonly StringBuilder builder = new StringBuilder(); // Later... builder = null; // Not allowed - you can''t change the *variable* // Allowed - changes the contents of the *object* to which the value // of builder refers. builder.Append("Foo");

Vea mi artículo sobre referencias y valores para más información.


No puede modificar una colección mientras se enumera. El segundo ejemplo solo actualiza una propiedad del objeto, que es completamente diferente.

Use un ciclo for si necesita agregar / eliminar / modificar elementos en una colección:

for (int i = 0; i < MyObjectList.Count; i++) { MyObjectList[i] = new MyObject(); }


Porque el primero no tiene mucho sentido, básicamente. El elemento variable está controlado por el iterador (establecido en cada iteración). No debería necesitar cambiarlo, solo use otra variable:

foreach (var item in MyObjectList) { var someOtherItem = Value; .... }

En cuanto al segundo, existen casos de uso válidos allí; es posible que desee repetir una enumeración de automóviles y llamar a .Drive () en cada uno, o establecer car.gasTank = full;


Porque los dos no son lo mismo . Al hacer lo primero, puede esperar cambiar el valor que está en la colección. Pero la forma en que foreach funciona, no hay manera de que se haya podido hacer. Solo puede recuperar elementos de la colección, no establecerlos.

Pero una vez que tienes el objeto, es un objeto como cualquier otro y puedes modificarlo de cualquier forma (permitida) que puedas.


Si observa las especificaciones del lenguaje, puede ver por qué esto no funciona:

Las especificaciones dicen que un foreach se expande al siguiente código:

E e = ((C)(x)).GetEnumerator(); try { V v; while (e.MoveNext()) { v = (V)(T)e.Current; embedded-statement } } finally { … // Dispose e }

Como puede ver, el elemento actual se usa para llamar a MoveNext () uno. Entonces, si cambia el elemento actual, el código se ''pierde'' y no puede iterar sobre la colección. Así que cambiar el elemento a otra cosa no tiene ningún sentido si ves qué código está produciendo realmente el compilador.


Use for loop en lugar de foreach loop y asigne valor. funcionará

foreach (var a in FixValue) { for (int i = 0; i < str.Count(); i++) { if (a.Value.Contains(str[i])) str[i] = a.Key; } }


foreach es un iterador de solo lectura que repite dinámicamente las clases que implementan IEnumerable, cada ciclo de foreach llamará a IEnumerable para obtener el siguiente elemento, el elemento que tiene es de solo lectura, no puede volver a asignarlo, sino que simplemente llama al item.Value está accediendo a él y asignando algún valor a un atributo de lectura / escritura y, aún así, la referencia del elemento es una referencia de solo lectura.


Sería posible hacer que el item mutable. Podríamos cambiar la forma en que se produce el código para que:

foreach (var item in MyObjectList) { item = Value; }

Se convirtió en equivalente a:

using(var enumerator = MyObjectList.GetEnumerator()) { while(enumerator.MoveNext()) { var item = enumerator.Current; item = Value; } }

Y luego compilaría. Sin embargo, no afectaría la colección.

Y ahí está el problema. El código:

foreach (var item in MyObjectList) { item = Value; }

Tiene dos formas razonables para que un ser humano lo piense. Una es que ese item es solo un titular de lugar y cambiarlo no es diferente al cambio de item en:

for(int item = 0; item < 100; item++) item *= 2; //perfectly valid

El otro es que cambiar el artículo realmente cambiaría la colección.

En el primer caso, podemos simplemente asignar el elemento a otra variable, y luego jugar con eso, por lo que no hay pérdida. En el último caso, esto está prohibido (o al menos, no se puede esperar alterar una colección mientras se itera a través de ella, aunque no es obligatorio que todos los enumeradores lo apliquen) y en muchos casos es imposible proporcionarla (dependiendo de la naturaleza del enumerable).

Incluso si consideramos que el primer caso es la implementación "correcta", el hecho de que pueda ser razonablemente interpretado por humanos de dos maneras diferentes es una razón suficientemente buena para evitar permitirlo, especialmente si tenemos en cuenta que podemos evitarlo en cualquier caso. .