una ultimo recorrer obtener objetos lista eliminar elementos elemento buscar agregar c# .net

c# - ultimo - Modificación directa de elementos List<T>



obtener elemento de una lista c# (6)

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 una matriz, funciona:

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

Pero al usar List, no compila:

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

¿Por qué?


El compilador C # le 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 valores, de modo que cuando accede a un elemento de lista, de hecho tendrá acceso 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. Como el valor no se conserva, el valor no cambiará.

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 haces que Map sea una clase, puedes usar una lista para almacenar tu elemento. A continuación, obtendrá una referencia a un objeto Map 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<Map> list = new List<Map>() { new Map(10), new Map(20), new Map(30), new Map(40) }; Map map = list[2]; map.Size = 42; list[2] = map;


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

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


No soy un desarrollador de XNA, por lo que no puedo comentar qué tan estricto es el requisito de usar estructuras frente a clases para XNA. Pero este parece ser un lugar mucho más natural para usar una clase con el fin de obtener la semántica pass-by-ref que está buscando.

Lo único que pensé es que podrías hacer uso del boxeo. Al crear una interfaz, por ejemplo, ''IMap'' en su caso, para ser implantada por su estructura ''Mapa'', y luego usando una List<IMap> , la lista mantendrá 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 podría ser llamado por lo siguiente:

List<IMap> list = new List<IMap>() { 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 una vez, cuando se pasen a la Lista en primer lugar, y NO cuando se las llame usando 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 regresan a Map (copiándolo fuera de List<IMap> ) en otras partes de tu código.

Aunque esto lograría su objetivo de tener acceso directo de lectura-escritura a las estructuras dentro de la Lista <>, encajonar la estructura realmente está rellenando la estructura en una clase (un Objeto del Sistema) y, por lo tanto, creo que podría hacer más sentido para hacer que tu ''Mapa'' sea una clase en primer lugar?

Micro


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

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

private void SomeFunction() { List<VertexBasicTerrain> 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<T> private void CalculateNormals(List<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(); }


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

Al usar una estructura, es mejor hacerlos inmutables. Esto evitará efectos como este.

Al usar una matriz, tiene acceso directo a las estructuras de memoria. Al usar la Lista <T> .get_Item trabajas en un valor de retorno, es decir, una copia de la estructura.

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

Además, usar List <T> .ToArray no lo resuelve, porque creará una copia de la matriz interna y devolverá esto.

Y este no es el único lugar donde obtendrá efectos como este cuando use una estructura.

La solución provista por Divo es una muy buena solución. Pero debe recordar trabajar de esta manera, no solo cuando usa la Lista <T>, sino que también desea cambiar un campo dentro de la estructura.


list [ 2 ].Size = 0;

es de hecho:

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

Usa la class en lugar de struct .