Eu já vi ótimas perguntas sobre tópicos semelhantes, mas nenhuma que abordava esse método específico:
Como tenho várias coleções de entidades de jogo no meu jogo [XNA Game Studio], com muitas entidades pertencentes a várias listas, estou pensando em maneiras de acompanhar sempre que uma entidade é destruída e removê-la das listas às quais pertence. .
Muitos métodos em potencial parecem desleixados / complicados, mas me lembro de uma maneira que já vi antes, na qual, em vez de ter várias coleções de entidades de jogos, você tem coleções de IDs de entidades de jogos . Esses IDs são mapeados para entidades do jogo por meio de um "banco de dados" central (talvez apenas uma tabela de hash).
Portanto, sempre que qualquer parte do código quiser acessar os membros de uma entidade do jogo, ele primeiro verifica se ainda está no banco de dados. Caso contrário, pode reagir em conformidade.
Esta é uma abordagem sólida? Parece que isso eliminaria muitos dos riscos / aborrecimentos de armazenar várias listas, com a troca sendo o custo da pesquisa toda vez que você deseja acessar um objeto.
fonte
Tudo o que você fez foi transferir o ônus de executar a verificação desde o momento da destruição até o momento do uso. Eu não diria que nunca fiz isso antes, mas não considero 'som'.
Sugiro usar uma das várias alternativas mais robustas. Se o número de listas for conhecido, você poderá simplesmente remover o objeto de todas as listas. Isso é inofensivo se o objeto não estiver em uma determinada lista e você garantiu que o removeu corretamente.
Se o número de listas for desconhecido ou impraticável, armazene-as de volta no objeto. A implementação típica disso é o padrão de observador , mas você provavelmente pode obter um efeito semelhante com os delegados, que retornam a chamada para remover o item de qualquer lista que contenha quando necessário.
Ou, às vezes, você realmente não precisa de várias listas. Geralmente, você pode manter um objeto em apenas uma lista e usar consultas dinâmicas para gerar outras coleções transitórias quando precisar delas. por exemplo. Em vez de ter uma lista separada para o inventário de um personagem, você pode simplesmente retirar todos os objetos onde "transportado" é igual ao personagem atual. Isso pode parecer bastante ineficiente, mas é bom o suficiente para bancos de dados relacionais e pode ser otimizado por uma indexação inteligente.
fonte
Essa parece ser apenas uma instância mais específica do problema "como remover objetos de uma lista, enquanto itera através dela", que é fácil de resolver (iterar na ordem inversa é provavelmente a maneira mais simples de uma lista no estilo de matriz como C # 's
List
)Se uma entidade for referenciada de vários locais, ela deverá ser um tipo de referência de qualquer maneira. Portanto, você não precisa passar por um sistema de identificação complicado - uma classe em C # já fornece o indireto necessário.
E finalmente - por que se preocupar em "verificar o banco de dados"? Basta dar uma
IsDead
bandeira a cada entidade e verificar isso.fonte
IDisposable
padrão, que é muito semelhante à marcação de um objeto comoIsDead
. Obviamente, se você precisar agrupar instâncias, em vez de tê-las em GC, será necessária uma estratégia mais complicada.ObjectDisposedException
(que geralmente "trava" com uma exceção não tratada). Movendo a verificação para o próprio objeto (em vez de verificar o objeto externamente), você permite que o objeto defina seu próprio comportamento "estou morto" e remova o ônus da verificação dos chamadores. Para seus propósitos, você pode implementarIDisposable
ou criar sua própriaIsDead
bandeira com funcionalidade semelhante.Costumo usar essa abordagem em meu próprio jogo C # /. NET. Além dos outros benefícios (e riscos!) Descritos aqui, também pode ajudar a evitar problemas de serialização.
Se você deseja aproveitar os recursos internos de serialização binária do .NET Framework, o uso de IDs de entidade pode ajudar a minimizar o tamanho do gráfico de objeto gravado. Por padrão, o formatador binário do .NET serializa um gráfico inteiro de objetos no nível do campo. Digamos que eu queira serializar uma
Ship
instância. SeShip
houver um_owner
campo referenciandoPlayer
quem é o proprietário, essaPlayer
instância também será serializada. SePlayer
contiver um_ships
campo (de, digamosICollection<Ship>
), todos os navios do jogador também serão gravados, juntamente com outros objetos referenciados no nível do campo (recursivamente). É fácil serializar acidentalmente um grande gráfico de objetos quando você deseja serializar apenas uma pequena parte dele.Se, em vez disso, tenho um
_ownerId
campo, posso usar esse valor para resolver aPlayer
referência sob demanda. Minha API pública pode até permanecer inalterada, com aOwner
propriedade simplesmente executando a pesquisa.Embora as pesquisas baseadas em hash sejam geralmente muito rápidas, a sobrecarga adicional pode se tornar um problema para conjuntos de entidades muito grandes com pesquisas frequentes. Se isso se tornar um problema para você, você poderá armazenar em cache a referência usando um campo que não seja serializado. Por exemplo, você poderia fazer algo assim:
Claro, essa abordagem também pode fazer a serialização mais difícil quando você realmente quer para serializar um gráfico de objeto grande. Nesses casos, você precisaria criar algum tipo de 'contêiner' para garantir que todos os objetos necessários sejam incluídos.
fonte
Outra maneira de lidar com sua limpeza é inverter o controle e usar um objeto Descartável. Você provavelmente tem um fator que aloca suas entidades; portanto, passe para a fábrica todas as listas às quais deve ser adicionado. A entidade mantém uma referência a todas as listas das quais faz parte e implementa IDisposable. No método de descarte, ele se remove de todas as listas. Agora você só precisa se preocupar com o gerenciamento de listas em dois locais: fábrica (criação) e descarte. Excluir no objeto é tão simples quanto entity.Dispose ().
A questão de nomes versus referências em C # é realmente importante apenas para gerenciar componentes ou tipos de valor. Se você possui listas de componentes vinculados a uma entidade, pode ser útil usar um campo de ID como chave de hashmap. Se você apenas possui listas de entidades, use listas com uma referência. As referências de C # são seguras e simples de trabalhar.
Para remover ou adicionar itens da lista, você pode usar uma Fila ou qualquer número de outras soluções para lidar com a adição e exclusão de listas.
fonte