Ich habe großartige Fragen zu ähnlichen Themen gesehen, aber keine, die sich mit dieser speziellen Methode befassten:
Angesichts der Tatsache, dass ich in meinem [XNA Game Studio] -Spiel mehrere Sammlungen von Spielentitäten habe, wobei viele Entitäten zu mehreren Listen gehören, überlege ich, wie ich verfolgen kann, wann immer eine Entität zerstört wird, und sie aus den Listen entfernen kann, zu denen sie gehört .
Viele potenzielle Methoden scheinen schlampig / verworren zu sein, aber ich erinnere mich an eine Art und Weise, die ich zuvor gesehen habe, bei der Sie statt mehrerer Sammlungen von Spielentitäten Sammlungen von Spielentitäts- IDs haben . Diese IDs werden Spielentitäten über eine zentrale "Datenbank" (möglicherweise nur eine Hash-Tabelle) zugeordnet.
Wenn also ein Teil des Codes auf die Mitglieder einer Spielentität zugreifen möchte, prüft er zunächst, ob er sich noch in der Datenbank befindet. Wenn nicht, kann es entsprechend reagieren.
Ist das ein vernünftiger Ansatz? Es scheint, dass dies viele der Risiken und Probleme beim Speichern mehrerer Listen beseitigen würde, wobei der Kompromiss die Kosten für die Suche jedes Mal sind, wenn Sie auf ein Objekt zugreifen möchten.
quelle
Alles, was Sie getan haben, ist, die Last der Durchführung der Überprüfung vom Zeitpunkt der Zerstörung auf den Zeitpunkt der Verwendung zu verlagern. Ich würde nicht behaupten, dass ich das noch nie zuvor gemacht habe, aber ich halte es nicht für "Klang".
Ich schlage vor, eine von mehreren robusteren Alternativen zu verwenden. Wenn die Anzahl der Listen bekannt ist, können Sie das Objekt einfach aus allen Listen entfernen. Das ist harmlos, wenn sich das Objekt nicht in einer bestimmten Liste befindet und Sie es garantiert korrekt entfernt haben.
Wenn die Anzahl der Listen unbekannt oder unpraktisch ist, speichern Sie Rückverweise auf diese im Objekt. Die typische Implementierung hierfür ist das Beobachtermuster. Sie können jedoch wahrscheinlich einen ähnlichen Effekt mit Delegaten erzielen, die zurückrufen, um das Element bei Bedarf aus den enthaltenen Listen zu entfernen.
Oder manchmal brauchen Sie gar nicht mehrere Listen. Oft können Sie ein Objekt in nur einer Liste behalten und dynamische Abfragen verwenden, um bei Bedarf andere vorübergehende Sammlungen zu generieren. z.B. Anstatt eine separate Liste für das Inventar eines Charakters zu haben, können Sie einfach alle Objekte herausziehen, bei denen "Mitgenommen" dem aktuellen Charakter entspricht. Dies mag ziemlich ineffizient klingen, ist aber für relationale Datenbanken gut genug und kann durch clevere Indizierung optimiert werden.
quelle
Dies scheint nur eine spezifischere Instanz des Problems "So entfernen Sie Objekte aus einer Liste, während Sie sie durchlaufen" zu sein, das leicht zu lösen ist (das Iterieren in umgekehrter Reihenfolge ist wahrscheinlich der einfachste Weg für eine Liste im Array-Stil wie C #
List
).Wenn eine Entität von mehreren Stellen aus referenziert werden soll, sollte es sich ohnehin um einen Referenztyp handeln . Sie müssen also kein kompliziertes ID-System durchlaufen - eine Klasse in C # bietet bereits die erforderliche Indirektion.
Und schließlich - warum "die Datenbank überprüfen"? Geben Sie jeder Entität einfach ein
IsDead
Flag und überprüfen Sie dies.quelle
IDisposable
Muster, das dem Markieren eines Objekts als sehr ähnlich istIsDead
. Wenn Sie Instanzen bündeln müssen, anstatt sie GC-fähig zu machen, ist natürlich eine kompliziertere Strategie erforderlich.ObjectDisposedException
(was im Allgemeinen mit einer unhandlichen Ausnahme "abstürzt"). Indem Sie die Prüfung auf das Objekt selbst verschieben (anstatt das Objekt extern zu prüfen), können Sie dem Objekt erlauben, sein eigenes Verhalten "Bin ich tot" zu definieren und die Last der Prüfung von Anrufern zu entfernen. Für Ihre Zwecke können Sie einIDisposable
eigenesIsDead
Flag mit ähnlichen Funktionen implementieren oder erstellen .Ich benutze diesen Ansatz oft in meinem eigenen C # /. NET-Spiel. Abgesehen von den anderen hier beschriebenen Vorteilen (und Gefahren!) Kann dies auch dazu beitragen, Serialisierungsprobleme zu vermeiden.
Wenn Sie die integrierten binären Serialisierungsfunktionen von .NET Framework nutzen möchten, können Sie mithilfe der Entitäts-IDs die Größe des ausgeschriebenen Objektdiagramms minimieren. Standardmäßig serialisiert der Binärformatierer von .NET ein gesamtes Objektdiagramm auf Feldebene. Angenommen, ich möchte eine
Ship
Instanz serialisieren . WennShip
ein_owner
Feld auf denPlayer
Eigentümer verweist , wird diesePlayer
Instanz ebenfalls serialisiert. WennPlayer
ein_ships
Feld enthält (z. B.ICollection<Ship>
), dann alle Schiffe des Spielers zusammen mit allen anderen Objekten, auf die auf Feldebene verwiesen wird, ebenfalls ausgeschrieben (rekursiv). Es ist einfach, versehentlich ein großes Objektdiagramm zu serialisieren, wenn Sie nur einen kleinen Teil davon serialisieren möchten.Wenn ich stattdessen ein
_ownerId
Feld habe, kann ich diesen Wert verwenden, um diePlayer
Referenz bei Bedarf aufzulösen . Meine öffentliche API kann mit dem sogar unverändert bleibenOwner
Eigenschaft lediglich die Suche durchführt.Während Hash-basierte Suchvorgänge im Allgemeinen sehr schnell sind, kann der zusätzliche Overhead bei sehr großen Entitätssätzen mit häufigen Suchvorgängen zu einem Problem werden. Wenn es für Sie zu einem Problem wird, können Sie die Referenz mithilfe eines Felds zwischenspeichern, das nicht serialisiert wird. Zum Beispiel könnten Sie so etwas tun:
Natürlich kann dieser Ansatz auch die Serialisierung erschweren , wenn Sie tatsächlich ein großes Objektdiagramm serialisieren möchten . In diesen Fällen müssten Sie eine Art 'Container' entwickeln, um sicherzustellen, dass alle erforderlichen Objekte enthalten sind.
quelle
Eine andere Möglichkeit, Ihre Bereinigung durchzuführen, besteht darin, die Kontrolle umzukehren und ein Einwegobjekt zu verwenden. Sie haben wahrscheinlich einen Faktor, der Ihre Entitäten zuordnet. Übergeben Sie daher alle Listen, zu denen sie hinzugefügt werden sollen, an die Factory. Die Entität behält einen Verweis auf alle Listen bei, zu denen sie gehört, und implementiert IDisposable. Bei der dispose-Methode wird es aus allen Listen entfernt. Jetzt müssen Sie sich nur noch an zwei Stellen mit der Listenverwaltung befassen: Fabrik (Erstellung) und Entsorgung. Das Löschen im Objekt ist so einfach wie entity.Dispose ().
Das Problem von Namen und Referenzen in C # ist wirklich nur für die Verwaltung von Komponenten oder Werttypen wichtig. Wenn Sie Listen von Komponenten haben, die mit einer Entität verknüpft sind, kann die Verwendung eines ID-Felds als Hashmap-Schlüssel hilfreich sein. Wenn Sie jedoch nur Listen mit Entitäten haben, verwenden Sie einfach Listen mit einer Referenz. C # -Referenzen sind sicher und einfach zu bearbeiten.
Um Elemente aus der Liste zu entfernen oder hinzuzufügen, können Sie eine Warteschlange oder eine beliebige Anzahl anderer Lösungen zum Hinzufügen und Löschen von Listen verwenden.
quelle