J'ai vu de grandes questions sur des sujets similaires, mais aucune n'a abordé cette méthode particulière:
Étant donné que j'ai plusieurs collections d'entités de jeu dans mon jeu [XNA Game Studio], avec de nombreuses entités appartenant à plusieurs listes, j'envisage des moyens de garder une trace de chaque fois qu'une entité est détruite et de la supprimer des listes auxquelles elle appartient .
Beaucoup de méthodes potentielles semblent bâclées / compliquées, mais je me souviens d'une manière que j'ai vue auparavant dans laquelle, au lieu d'avoir plusieurs collections d'entités de jeu, vous avez à la place des collections d' ID d' entité de jeu . Ces ID sont mappés aux entités de jeu via une "base de données" centrale (peut-être juste une table de hachage).
Ainsi, chaque fois qu'un morceau de code veut accéder aux membres d'une entité de jeu, il vérifie d'abord s'il est encore dans la base de données. Sinon, il peut réagir en conséquence.
Est-ce une bonne approche? Il semble que cela éliminerait de nombreux risques / tracas liés au stockage de plusieurs listes, le compromis étant le coût de la recherche chaque fois que vous souhaitez accéder à un objet.
la source
Tout ce que vous avez fait, c'est déplacer la charge d'effectuer le contrôle du moment de la destruction au moment de l'utilisation. Je ne dirais pas que je n'ai jamais fait ça auparavant, mais je ne le considère pas comme «sonore».
Je suggère d'utiliser l'une des nombreuses alternatives plus robustes. Si le nombre de listes est connu, vous pouvez vous en sortir en supprimant simplement l'objet de toutes les listes. Cela est inoffensif si l'objet ne se trouve pas dans une liste donnée et que vous êtes assuré de l'avoir supprimé correctement.
Si le nombre de listes est inconnu ou impraticable, stockez-y des références arrières sur l'objet. L'implémentation typique de ceci est le modèle d'observateur , mais vous pouvez probablement obtenir un effet similaire avec les délégués, qui rappellent de supprimer l'élément de toutes les listes contenant si nécessaire.
Ou parfois, vous n'avez pas vraiment besoin de plusieurs listes. Souvent, vous pouvez conserver un objet dans une seule liste et utiliser des requêtes dynamiques pour générer d'autres collections transitoires lorsque vous en avez besoin. par exemple. Au lieu d'avoir une liste distincte pour l'inventaire d'un personnage, vous pouvez simplement retirer tous les objets où "porté" est égal au personnage actuel. Cela peut sembler assez inefficace, mais c'est assez bon pour les bases de données relationnelles et peut être optimisé par une indexation intelligente.
la source
Cela semble être juste une instance plus spécifique du problème "comment supprimer des objets d'une liste, tout en itérant à travers elle", qui est facile à résoudre (l'itération dans l'ordre inverse est probablement le moyen le plus simple pour une liste de style tableau comme C #
List
).Si une entité doit être référencée à partir de plusieurs endroits, elle doit de toute façon être un type de référence . Vous n'avez donc pas à passer par un système d'identification compliqué - une classe en C # fournit déjà l'indirection nécessaire.
Et enfin - pourquoi s'embêter à "vérifier la base de données"? Donnez simplement un
IsDead
drapeau à chaque entité et vérifiez-le.la source
IDisposable
modèle, qui est très similaire au marquage d'un objet commeIsDead
. De toute évidence, si vous avez besoin de regrouper des instances, plutôt que de les faire GC, alors une stratégie plus compliquée est nécessaire.ObjectDisposedException
(ce qui se "bloque" généralement avec une exception non gérée). En déplaçant la vérification vers l'objet lui-même (plutôt que de vérifier l'objet en externe), vous autorisez l'objet à définir son propre comportement "suis-je mort" et supprimez la charge de la vérification des appelants. Pour vos besoins, vous pouvez implémenterIDisposable
ou créer votre propreIsDead
indicateur avec des fonctionnalités similaires.J'utilise souvent cette approche dans mon propre jeu C # /. NET. Outre les autres avantages (et dangers!) Décrits ici, cela peut également aider à éviter les problèmes de sérialisation.
Si vous souhaitez tirer parti des fonctionnalités de sérialisation binaires intégrées du .NET Framework, l'utilisation d'ID d'entité peut aider à réduire la taille du graphique d'objet qui est écrit. Par défaut, le formateur binaire de .NET sérialise un graphique d'objet entier au niveau du champ. Disons que je veux sérialiser une
Ship
instance. SiShip
un_owner
champ fait référence àPlayer
qui le possède, cettePlayer
instance sera également sérialisée. S'ilPlayer
contient un_ships
champ (de, disonsICollection<Ship>
), tous les vaisseaux du joueur seront également écrits, ainsi que tous les autres objets référencés au niveau du champ (récursivement). Il est facile de sérialiser accidentellement un énorme graphique d'objet lorsque vous ne voulez sérialiser qu'une petite partie de celui-ci.Si, à la place, j'ai un
_ownerId
champ, je peux utiliser cette valeur pour résoudre laPlayer
référence à la demande. Mon API publique peut même rester inchangée, laOwner
propriété effectuant simplement la recherche.Bien que les recherches basées sur le hachage soient généralement très rapides, la surcharge supplémentaire pourrait devenir un problème pour les très grands ensembles d'entités avec des recherches fréquentes. Si cela devient un problème pour vous, vous pouvez mettre en cache la référence à l'aide d'un champ qui n'est pas sérialisé. Par exemple, vous pouvez faire quelque chose comme ceci:
Bien sûr, cette approche peut également rendre la sérialisation plus difficile lorsque vous souhaitez sérialiser un graphe d'objets volumineux. Dans ces cas, vous auriez besoin de concevoir une sorte de «conteneur» pour vous assurer que tous les objets nécessaires sont inclus.
la source
Une autre façon de gérer votre nettoyage consiste à inverser le contrôle et à utiliser un objet jetable. Vous avez probablement un facteur affectant vos entités, alors passez à l'usine toutes les listes auxquelles il doit être ajouté. L'entité conserve une référence à toutes les listes dont elle fait partie et implémente IDisposable. Dans la méthode dispose, il se supprime de toutes les listes. Il ne vous reste plus qu'à vous préoccuper de la gestion des listes en deux endroits: l'usine (création) et l'élimination. La suppression de l'objet est aussi simple que entity.Dispose ().
La question des noms et des références en C # n'est vraiment importante que pour la gestion des composants ou des types de valeurs. Si vous avez des listes de composants liés à une entité, l'utilisation d'un champ ID comme clé de hachage peut être utile. Si vous n'avez que des listes d'entités, utilisez simplement des listes avec une référence. Les références C # sont sûres et simples à utiliser.
Pour supprimer ou ajouter des éléments de la liste, vous pouvez utiliser une file d'attente ou un certain nombre d'autres solutions pour gérer l'ajout et la suppression de listes.
la source