vistas una tablas rendimiento recomendaciones queries porque optimizador lenta las dañan cuello consulta consejos como botella agilizar c# foreach immutability value-type

tablas - En C#, ¿por qué no puedo modificar el miembro de una instancia de tipo de valor en un ciclo foreach?



view lenta mysql (7)

Sé que los tipos de valores deben ser inmutables, pero eso es solo una sugerencia, no una regla, ¿verdad? Entonces, ¿por qué no puedo hacer algo como esto?

struct MyStruct { public string Name { get; set; } } public class Program { static void Main(string[] args) { MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } }; foreach (var item in array) { item.Name = "3"; } //for (int i = 0; i < array.Length; i++) //{ // array[i].Name = "3"; //} Console.ReadLine(); } }

El bucle foreach en el código no se compila mientras que el bucle comentado funciona bien. El mensaje de error:

No se pueden modificar los miembros de ''elemento'' porque es una ''variable de iteración foreach''

¿Porqué es eso?


Convierte MyStruct en una clase (en lugar de struct) y podrás hacerlo.



Es una sugerencia en el sentido de que no hay nada que te impida violarla, pero en realidad debería tener mucho más peso que "es una sugerencia". Por ejemplo, por las razones que estás viendo aquí.

Los tipos de valor almacenan el valor real en una variable, no una referencia. Eso significa que tiene el valor en su matriz, y tiene una copia de ese valor en el item , no una referencia. Si se le permitiera cambiar los valores en el item , no se reflejaría en el valor de la matriz, ya que es un valor completamente nuevo. Es por eso que no está permitido.

Si desea hacer esto, tendrá que recorrer la matriz por índice y no usar una variable temporal.


Las estructuras son tipos de valores.
Las clases son tipos de referencia.

ForEach construcción utiliza IEnumerator de elementos de tipo de datos IEnumerable . Cuando eso sucede, las variables son de solo lectura en el sentido de que no puede modificarlas, y como tiene un tipo de valor, no puede modificar ningún valor contenido, ya que comparte la misma memoria.

C # lang spec section 8.8.4:

La variable de iteración corresponde a una variable local de solo lectura con un alcance que se extiende sobre la instrucción incrustada

Para arreglar esta clase de uso insted de estructura;

class MyStruct { public string Name { get; set; } }

Editar: @CuiPengFei

Si usa var implicit type, es más difícil para el compilador ayudarle. Si usara MyStruct , le diría en caso de estructura que es de solo lectura. En el caso de las clases, la referencia al elemento es de solo lectura, por lo que no puede escribir item = null; dentro del ciclo, pero puede cambiar sus propiedades, que son mutables.

También puede usar (si le gusta usar struct ):

MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } }; for (int index=0; index < array.Length; index++) { var item = array[index]; item.Name = "3"; }


Los tipos de valores se llaman por valor . En resumen, cuando evalúa la variable, se realiza una copia. Incluso si esto estuviera permitido, estaría editando una copia, en lugar de la instancia original de MyStruct .

foreach es solo de lectura en C # . Para los tipos de referencia (clase), esto no cambia mucho, ya que solo la referencia es de solo lectura, por lo que todavía puede hacer lo siguiente:

MyClass[] array = new MyClass[] { new MyClass { Name = "1" }, new MyClass { Name = "2" } }; foreach ( var item in array ) { item.Name = "3"; }

Sin embargo, para los tipos de valor (struct), el objeto completo es de solo lectura, lo que resulta en lo que está experimentando. No puedes ajustar el objeto en el foreach.


Porque foreach usa un enumerador, y los enumeradores no pueden cambiar la colección subyacente, pero pueden , sin embargo, cambiar cualquier objeto referenciado por un objeto en la colección. Aquí es donde entra en juego la semántica de Value y Reference.

En un tipo de referencia, es decir, una clase, toda la colección que se almacena es una referencia a un objeto. Como tal, en realidad nunca toca a ninguno de los miembros del objeto, y no podría importarle menos. Un cambio en el objeto no tocará la colección.

Por otro lado, los tipos de valor almacenan toda su estructura en la colección. No puede tocar a sus miembros sin cambiar la colección e invalidar el enumerador.

Además, el enumerador devuelve una copia del valor en la colección. En un tipo de ref, esto no significa nada. Una copia de una referencia será la misma referencia, y puede cambiar el objeto al que se hace referencia de la manera que desee con los cambios que se extienden fuera del alcance. En un tipo de valor, por otro lado, significa que todo lo que obtiene es una copia del objeto y, por lo tanto, cualquier cambio en dicha copia nunca se propagará.


NOTA: Según el comentario de Adam, esta no es realmente la respuesta / causa correcta del problema. Sin embargo, todavía es algo que vale la pena tener en cuenta.

De MSDN . No puede modificar los valores cuando usa el Enumerador, que es esencialmente lo que Foreach está haciendo.

Los enumeradores se pueden usar para leer los datos en la colección, pero no se pueden usar para modificar la colección subyacente.

Un enumerador sigue siendo válido siempre que la colección permanezca sin cambios. Si se realizan cambios en la colección, como agregar, modificar o eliminar elementos, el enumerador queda irrevocablemente invalidado y su comportamiento no está definido.