Modificando directamente los elementos de la lista

Tengo esta estructura

struct Map { public int Size; public Map ( int size ) { this.Size = size; } public override string ToString ( ) { return String.Format ( "Size: {0}", this.Size ); } } 

Cuando se usa array, funciona:

 Map [ ] arr = new Map [ 4 ] { new Map(10), new Map(20), new Map(30), new Map(40)}; arr [ 2 ].Size = 0; 

Pero cuando se usa Lista, no se comstack:

 List list = new List ( ) { new Map(10), new Map(20), new Map(30), new Map(40)}; list [ 2 ].Size = 0; 

¿Por qué?

El comstackdor de C # te dará el siguiente error:

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

La razón es que las estructuras son tipos de valor, de modo que cuando acceda a un elemento de lista, de hecho accederá a una copia intermedia del elemento que ha sido devuelto por el indexador de la lista.

Desde MSDN :

Mensaje de error

No se puede modificar el valor de retorno de ‘expresión’ porque no es una variable

Se intentó modificar un tipo de valor que fue el resultado de una expresión intermedia. Debido a que el valor no se mantiene, el valor no se modificará.

Para resolver este error, almacene el resultado de la expresión en un valor intermedio o use un tipo de referencia para la expresión intermedia.

Soluciones:

  1. Usa una matriz. Esto le da acceso directo a los elementos (no está accediendo a una copia)
  2. Cuando crea una clase en el Mapa, puede usar una Lista para almacenar su elemento. A continuación, obtendrá una referencia a un objeto Mapa en lugar de una copia intermedia y podrá modificar el objeto.
  3. Si no puede cambiar Map from struct a una clase, debe guardar el elemento de la lista en una variable temporal:
 List list = new List() { new Map(10), new Map(20), new Map(30), new Map(40) }; Map map = list[2]; map.Size = 42; list[2] = map; 

Debido a que es una struct , al usar la Lista , estás creando copias.

Cuando se usa una estructura, es mejor hacerlos inmutables. Esto evitará efectos como este.

Al usar una matriz, tiene acceso directo a las estructuras de memoria. Usando la Lista .get_Item, trabajará con un valor de retorno, es decir, una copia de la estructura.

Si fuera una clase, obtendría una copia de un puntero a la clase, pero no lo notaría, ya que los punteros están ocultos en C #.

También el uso de la Lista .ToArray no lo resuelve, ya que creará una copia de la matriz interna y devolverá esto.

Y este no es el único lugar donde obtendrás efectos como este al usar una estructura.

La solución provista por Divo es una muy buena solución. Pero debes recordar trabajar de esta manera, no solo cuando uses la Lista sino en todos los lugares donde quieras cambiar un campo dentro de la estructura.

No soy un desarrollador de XNA, por lo que no puedo comentar sobre lo estricto que es el requisito de usar estructuras y clases para XNA. Pero este parece ser un lugar mucho más natural para usar una clase para obtener la semántica paso a paso que está buscando.

Un pensamiento que tuve es que podrías hacer uso del boxeo. Al crear una interfaz, digamos ‘IMap’ en su caso, para ser implantada por su estructura ‘Map’, y luego utilizando una List , la lista contendrá los objetos System.Object, que se pasan por referencia. Por ejemplo:

 interface IMap { int Size { get; set; } } struct Map: IMap { public Map(int size) { _size = size; } private int _size; public int Size { get { return _size; } set { _size = value; } } public override string ToString() { return String.Format("Size: {0}", this.Size); } } 

Que luego podría ser llamado por el siguiente:

 List list = new List() { new Map(10), new Map(20), new Map(30), new Map(40)}; list[2].Size = 4; Console.WriteLine("list[2].Size = " + list[2].Size.ToString()); 

Tenga en cuenta que estas estructuras solo se incluirán en el cuadro una vez, cuando se pasen a la Lista en primer lugar, y NO cuando se las llame utilizando un código como ‘list [2] .Size = 4’, por lo que debería ser bastante eficiente, a menos que esté tomando estos objetos IMap y su conversión al Mapa (copiándolos fuera de la List ) en otras partes de su código.

Aunque esto lograría su objective de tener acceso directo de lectura y escritura a las estructuras dentro de la Lista <>, encajonar la estructura en realidad está metiendo la estructura en una clase (un Objeto.Sistema) y, por lo tanto, creo que podría hacer ¿Tiene más sentido hacer de su ‘Mapa’ una clase en primer lugar?

Micro

Sigo volviendo a esta pregunta cuando bash calcular normales en un búfer de vértice en XNA.

La mejor solución XNA que se me ocurrió fue copiar los datos (o almacenarlos) en una matriz.

 private void SomeFunction() { List vertexList = GenerateVertices(); short[] indexArray = GenerateIndices(); CalculateNormals(vertexList, ref indexArray); // Will not work var vertexArray = vertexList.ToArray(); CalculateNormals(ref vertexArray, ref indexArray); } // This works (algorithm from Reimers.net) private void CalculateNormals(ref VertexBasicTerrain[] vertices, ref short[] indices) { for (int i = 0; i < vertices.Length; i++) vertices[i].Normal = new Vector3(0, 0, 0); for (int i = 0; i < indices.Length / 3; i++) { Vector3 firstvec = vertices[indices[i * 3 + 1]].Position - vertices[indices[i * 3]].Position; Vector3 secondvec = vertices[indices[i * 3]].Position - vertices[indices[i * 3 + 2]].Position; Vector3 normal = Vector3.Cross(firstvec, secondvec); normal.Normalize(); vertices[indices[i * 3]].Normal += normal; vertices[indices[i * 3 + 1]].Normal += normal; vertices[indices[i * 3 + 2]].Normal += normal; } for (int i = 0; i < vertices.Length; i++) vertices[i].Normal.Normalize(); } // This does NOT work and throws a compiler error because of the List private void CalculateNormals(List vertices, ref short[] indices) { for (int i = 0; i < vertices.Length; i++) vertices[i].Normal = new Vector3(0, 0, 0); for (int i = 0; i < indices.Length / 3; i++) { Vector3 firstvec = vertices[indices[i * 3 + 1]].Position - vertices[indices[i * 3]].Position; Vector3 secondvec = vertices[indices[i * 3]].Position - vertices[indices[i * 3 + 2]].Position; Vector3 normal = Vector3.Cross(firstvec, secondvec); normal.Normalize(); vertices[indices[i * 3]].Normal += normal; vertices[indices[i * 3 + 1]].Normal += normal; vertices[indices[i * 3 + 2]].Normal += normal; } for (int i = 0; i < vertices.Length; i++) vertices[i].Normal.Normalize(); } 

Decidí reemplazar directamente el resultado en una copia y reasignar el resultado como:

 Map map = arr [ 2 ]; map.Size = 0; arr [ 2 ] = map; 
 list [ 2 ].Size = 0; 

es de hecho:

 //Copy of object Map map = list[2]; map.Size = 2; 

Usa la class en lugar de la struct .