He visto excelentes preguntas sobre temas similares, pero ninguna que aborda este método en particular:
Dado que tengo varias colecciones de entidades de juego en mi juego [XNA Game Studio], con muchas entidades que pertenecen a varias listas, estoy considerando formas de hacer un seguimiento de cada entidad que se destruye y eliminarla de las listas a las que pertenece .
Muchos métodos potenciales parecen descuidados / complicados, pero recuerdo una forma que he visto antes en la que, en lugar de tener múltiples colecciones de entidades de juego, tienes colecciones de ID de entidades de juego . Estas ID se asignan a entidades de juego a través de una "base de datos" central (quizás solo una tabla hash).
Entonces, cada vez que un fragmento de código quiere acceder a los miembros de una entidad de juego, primero verifica si aún está en la base de datos. Si no, puede reaccionar en consecuencia.
¿Es este un enfoque sólido? Parece que eliminaría muchos de los riesgos / molestias de almacenar múltiples listas, con el compromiso de ser el costo de la búsqueda cada vez que desea acceder a un objeto.
fuente
Todo lo que ha hecho es mover la carga de realizar la verificación desde el momento de la destrucción hasta el momento del uso. No diría que nunca he hecho esto antes, pero no lo considero 'sonido'.
Sugiero usar una de varias alternativas más robustas. Si se conoce el número de listas, puede salirse simplemente eliminando el objeto de todas las listas. Eso es inofensivo si el objeto no está en una lista dada, y se garantiza que lo ha eliminado correctamente.
Si el número de listas es desconocido o poco práctico, almacene referencias a ellas en el objeto. La implementación típica de esto es el patrón de observación , pero probablemente pueda lograr un efecto similar con los delegados, que vuelven a llamar para eliminar el elemento de cualquier lista que contenga cuando sea necesario.
O a veces realmente no necesitas listas múltiples en absoluto. A menudo, puede mantener un objeto en una sola lista y utilizar consultas dinámicas para generar otras colecciones transitorias cuando las necesite. p.ej. En lugar de tener una lista separada para el inventario de un personaje, puede extraer todos los objetos donde "arrastrado" es igual al personaje actual. Esto puede parecer bastante ineficiente, pero es lo suficientemente bueno para las bases de datos relacionales y puede optimizarse mediante una indexación inteligente.
fuente
Esto parece ser solo una instancia más específica del problema "cómo eliminar objetos de una lista, mientras se itera a través de él", que es fácil de resolver (iterar en orden inverso es probablemente la forma más sencilla de una lista de estilo de matriz como C # 's
List
)Si una entidad va a ser referenciada desde múltiples lugares, de todos modos debería ser un tipo de referencia . Por lo tanto, no tiene que pasar por un sistema de identificación complicado: una clase en C # ya proporciona la dirección indirecta necesaria.
Y finalmente, ¿por qué molestarse en "verificar la base de datos"? Simplemente dele una
IsDead
bandera a cada entidad y verifique eso.fuente
IDisposable
patrón, que es muy similar a marcar un objeto comoIsDead
. Obviamente, si necesita agrupar instancias, en lugar de tenerlas GC'd, entonces se requiere una estrategia más complicada.ObjectDisposedException
(que generalmente se "bloqueará" con una excepción no administrada). Al mover el cheque al objeto mismo (en lugar de verificar el objeto externamente), permite que el objeto defina su propio comportamiento de "estoy muerto" y elimine la carga de la verificación de las personas que llaman. Para sus propósitos, podría implementarIDisposable
o hacer su propiaIsDead
bandera con una funcionalidad similar.A menudo uso este enfoque en mi propio juego C # / .NET. Además de los otros beneficios (¡y riesgos!) Descritos aquí, también puede ayudar a evitar problemas de serialización.
Si desea aprovechar las funciones de serialización binaria incorporadas en .NET Framework, el uso de ID de entidad puede ayudar a minimizar el tamaño del gráfico de objeto que se escribe. Por defecto, el formateador binario de .NET serializará un gráfico de objeto completo a nivel de campo. Digamos que quiero serializar una
Ship
instancia. SiShip
tiene un_owner
campo que hace referencia alPlayer
propietario, entonces esaPlayer
instancia también se serializaría. SiPlayer
contiene un_ships
campo (de, digamos,ICollection<Ship>
), todas las naves del jugador también se escribirán, junto con cualquier otro objeto referenciado en el nivel del campo (recursivamente). Es fácil serializar accidentalmente un gran gráfico de objetos cuando solo desea serializar una pequeña parte de él.Si, en cambio, tengo un
_ownerId
campo, entonces puedo usar ese valor para resolver laPlayer
referencia a pedido. Mi API pública incluso puede permanecer sin cambios, con laOwner
propiedad simplemente realizando la búsqueda.Si bien las búsquedas basadas en hash son generalmente muy rápidas, la sobrecarga adicional podría convertirse en un problema para conjuntos de entidades muy grandes con búsquedas frecuentes. Si se convierte en un problema para usted, puede almacenar en caché la referencia utilizando un campo que no se serializa. Por ejemplo, podría hacer algo como esto:
Por supuesto, este enfoque también puede hacer que la serialización más difícil cuando realmente se desea serializar un gráfico de objetos de gran tamaño. En esos casos, necesitaría idear algún tipo de 'contenedor' para garantizar que se incluyan todos los objetos necesarios.
fuente
Otra forma de manejar su limpieza es invertir el control y usar un objeto desechable. Probablemente tenga un factor que asigne sus entidades, por lo que pase a la fábrica todas las listas a las que debe agregarse. La entidad mantiene una referencia a todas las listas de las que forma parte e implementa IDisposable. En el método de eliminación, se elimina de todas las listas. Ahora solo tiene que preocuparse por la gestión de listas en dos lugares: fábrica (creación) y disposición. Eliminar en el objeto es tan simple como entity.Dispose ().
La cuestión de los nombres frente a las referencias en C # solo es realmente importante para administrar componentes o tipos de valores. Si tiene listas de componentes que están vinculados a una entidad, puede ser útil usar un campo ID como clave de hashmap. Sin embargo, si solo tiene listas de entidades, simplemente use listas con una referencia. Las referencias de C # son seguras y fáciles de usar.
Para eliminar o agregar elementos de la lista, puede utilizar una Cola o cualquier otra solución para tratar de agregar y eliminar de las listas.
fuente