Cambiar el valor de un elemento en una lista de estructuras

Tengo una lista de estructuras y quiero cambiar un elemento. Por ejemplo :

MyList.Add(new MyStruct("john"); MyList.Add(new MyStruct("peter"); 

Ahora quiero cambiar un elemento:

 MyList[1].Name = "bob" 

Sin embargo, cada vez que bash hacer esto obtengo el siguiente error:

No se puede modificar el valor de retorno de System.Collections.Generic.List.this [int] ‘porque no es una variable

Si utilizo una lista de clases, el problema no ocurre.

Supongo que la respuesta tiene que ver con que las estructuras sean un tipo de valor.

Entonces, si tengo una lista de estructuras ¿debería tratarlas como de solo lectura ? Si necesito cambiar elementos en una lista, ¿debería usar clases y no estructuras?

 MyList[1] = new MyStruct("bob"); 

las estructuras en C # casi siempre deberían estar diseñadas para ser inmutables (es decir, no tienen forma de cambiar su estado interno una vez que se han creado).

En su caso, lo que quiere hacer es reemplazar toda la estructura en el índice de matriz especificado, no tratar de cambiar solo una propiedad o campo.

No exactamente. Diseñar un tipo como clase o estructura no debe ser impulsado por su necesidad de almacenarlo en colecciones 🙂 Debe mirar la ‘semántica’ necesaria

El problema que está viendo se debe a la semántica del tipo de valor. Cada variable / referencia de tipo de valor es una nueva instancia. Cuando tu dices

 Struct obItem = MyList[1]; 

lo que sucede es que se crea una nueva instancia de la estructura y todos los miembros se copian uno por uno. Para que tenga un clon de MyList [1], es decir, 2 instancias. Ahora bien, si modifica obItem, no afecta el original.

 obItem.Name = "Gishu"; // MyList[1].Name still remains "peter" 

Ahora ten paciencia conmigo por 2 minutos aquí (esto toma un tiempo para tragar … lo hizo por mí 🙂 Si realmente necesita estructuras para ser almacenadas en una colección y modificadas como usted indicó en su pregunta, tendrá que hacer su estructura expone una interfaz ( Sin embargo, esto dará como resultado el boxeo ). A continuación, puede modificar la estructura real a través de una referencia de interfaz, que se refiere al objeto en caja.

El siguiente fragmento de código ilustra lo que acabo de decir anteriormente

 public interface IMyStructModifier { String Name { set; } } public struct MyStruct : IMyStructModifier ... List obList = new List(); obList.Add(new MyStruct("ABC")); obList.Add(new MyStruct("DEF")); MyStruct temp = (MyStruct)obList[1]; temp.Name = "Gishu"; foreach (MyStruct s in obList) // => "ABC", "DEF" { Console.WriteLine(s.Name); } IMyStructModifier temp2 = obList[1] as IMyStructModifier; temp2.Name = "Now Gishu"; foreach (MyStruct s in obList) // => "ABC", "Now Gishu" { Console.WriteLine(s.Name); } 

HTH. Buena pregunta.
Actualización: @Hath – me hiciste correr para verificar si pasé por alto algo tan simple. (Sería inconsistente si las propiedades del setter no lo hacen y los métodos lo hicieron – el universo .Net todavía está equilibrado 🙂
El método Setter no funciona
obList2 [1] devuelve una copia cuyo estado se modificará. La estructura original en la lista permanece sin modificaciones. Entonces Set-via-Interface parece ser la única forma de hacerlo.

 List obList2 = new List(); obList2.Add(new MyStruct("ABC")); obList2.Add(new MyStruct("DEF")); obList2[1].SetName("WTH"); foreach (MyStruct s in obList2) // => "ABC", "DEF" { Console.WriteLine(s.Name); } 

No es tanto que las estructuras sean “inmutables”.

El verdadero problema subyacente es que las estructuras son un tipo de valor, no un tipo de referencia. Entonces, cuando saca una “referencia” de la lista de la lista, está creando una nueva copia de la estructura completa. Por lo tanto, cualquier cambio que realice en él está cambiando la copia, no la versión original en la lista.

Como dice Andrew, debes reemplazar toda la estructura. Como punto, creo que debes preguntarte por qué estás usando una estructura en primer lugar (en lugar de una clase). Asegúrate de no estar haciéndolo por preocupaciones de optimización prematura.

No hay nada de malo con las estructuras que tienen campos expuestos, o que permiten la mutación a través de los establecedores de propiedades. Sin embargo, las estructuras que se mutan a sí mismas en respuesta a métodos o captadores de propiedades son peligrosas porque el sistema permitirá que se invoquen métodos o captadores de propiedades en instancias de estructuras temporales; si los métodos o getters realizan cambios en la estructura, esos cambios terminarán siendo descartados.

Desafortunadamente, como usted nota, las colecciones integradas en .net son muy débiles para exponer los objetos de tipo valor que contiene. Tu mejor opción suele ser hacer algo como:

   MyStruct temp = myList [1];
   temp.Name = "Albert";
   myList [1] = temperatura;

Algo molesto, y nada en absoluto. Sigue siendo una mejora con respecto a una Lista de un tipo de clase, donde hacer lo mismo puede requerir:

   myList [1] .Name = "Albert";

pero también podría requerir:

   myList [1] = myList [1] .Withname ("Albert");

o tal vez

   myClass temp = (myClass) myList [1] .Clone ();
   temp.Name = "Albert";
   myList [1] = temperatura;

o tal vez alguna otra variación. Uno realmente no podría saber a menos que uno examinara myClass así como también el otro código que puso las cosas en la lista. Es muy posible que uno no pueda saber si el primer formulario es seguro sin examinar el código en ensamblajes a los que no se tiene acceso. Por el contrario, si Name es un campo expuesto de MyStruct, el método que proporcioné para actualizarlo funcionará, independientemente de qué más contenga MyStruct, o independientemente de lo que otras cosas hayan podido hacer con myList antes de que se ejecute el código o de qué pueden esperar. hazlo después