2009-05-28 7 views
7

Dies muss ein so häufiges Szenario sein, dass schon viel darüber geschrieben wurde, hoffentlich sogar ein wirklich gutes Muster. Ich habe ein Domänenmodell, in dem ein benutzerdefinierter Container Entitäten enthält. Zum Beispiel (Eigenschaften und Schnittstellen der Kürze halber nicht inbegriffen):Vermeiden Sie Zirkelverweis in Domain-Modell

class Entity 
{ 
    public int Id; 
    public EntityContainer ParentContainer; 
} 


class EntityContainer 
{ 
    public int Id; 
    public IList<Entity> Entities = new List<Entity>(); 

    public void AddEntity(Entity entity) 
    { 
     entity.ParentContainer = this; 
     Entities.Add(entity); 
    } 
} 


class Main 
{ 
    public Main() 
    { 
     Entity entity1 = new Entity(); 
     Entity entity2 = new Entity(); 
     EntityContainer entityContainer = new EntityContainer(); 
     entityContainer.AddEntity(entity1); 
     entityContainer.AddEntity(entity2); 

     // Can now traverse graph easily, e.g. 
     Console.WriteLine("entity1's parent container ID = " + entity1.ParentContainer.Id); 
     Console.WriteLine("Container contains at least this entity ID: " + entityContainer.Entities[0].Id); 

    } 
} 

kann ich jetzt meine Objektgraphen leicht in beide Richtungen durchlaufen, haben aber einen zirkulären Verweis erstellt. Würden Sie einen dritten Typ erstellen, um die Abhängigkeiten zu trennen?

Vielen Dank im Voraus

+2

Das Modell, das Sie dort haben, erlaubt keine umgekehrte Beziehung für mehr als einen übergeordneten Container, so dass es höchstwahrscheinlich nicht funktioniert, wenn Sie eine Entität in mehreren Containern haben. – workmad3

+0

können Sie klären, wie das ist kreisförmig? Sie erstellen eine Baumstruktur so weit ich es verstehe, also sehe ich nicht, woher die Zirkularität kommt. – RobV

+0

Entity hat einen Verweis auf EntityContainer und EntityContainer hat einen Verweis auf Entity. – ng5000

Antwort

4

Es ist nichts falsch mit zirkulären Referenzen, per se, und sie werden in großem Umfang in denen verwendet .NET Framework, z XmlNode.OwnerDocument, Control.Parent.

Wenn Sie den Baum nach oben durchlaufen müssen, können Sie einen Rückverweis verwenden.

In COM sind Zirkelverweise schwierig, denn wenn Sie den Container und alle seine untergeordneten Elemente auf nichts festlegen, werden Objekte nicht ordnungsgemäß bereinigt, da die untergeordneten Elemente weiterhin Verweise auf das übergeordnete Element enthalten. Die .NET Garbage Collection hat jedoch kein Problem damit, wie es implementiert ist.

+0

Ein Problem, auf das ich bei zirkulären Referenzen stoße, ist die Serialisierung. Im Allgemeinen, wenn Objekt A B und Objekt B A hat, wird es nicht serialisieren. Dies ist besonders problematisch, wenn diese Objekte in den Zustand der nicht-processionellen Sitzung versetzt werden. –

2

Braucht der Behälter über den Typ des Inhalts wissen? Wenn nicht, können Generika dies vermeiden - d. H. Container<T>, wo Sie Container<Entity> verwenden können, um zu verwenden. Andere als das; Die erforderlichen Details in eine Schnittstelle (oder Basisklasse) in einer Baugruppe zu übertragen, auf die beide verweisen können, ist ein üblicher Ansatz.

Persönlich würde ich versuchen, einfach die Notwendigkeit für das Kind zu vermeiden, über den Elternteil zu wissen.

Auch; Beachten Sie, dass wenn Sie tun gehen Sie die Abstraktion (Schnittstelle usw.) Route; Dies kann große Auswirkungen haben, wenn Sie beispielsweise die XML-Serialisierung verwenden.


(Kommentare bearbeiten) OK; Erstens: Welches Problem verursacht die zirkuläre Referenz (innerhalb einer Baugruppe)? wenn nicht, lass es in Ruhe. Wenn ein Problem auftritt, benötigen Sie einen zusätzlichen Typ. vermutlich einige Schnittstellen, die konkreten Typen darzustellen - also dort, wo Entity : IEntity und EntityContainer nur etwa IEntity (oder vv mit IEntityContainer oder beides) wissen,

+1

Das Kind muss aus Leistungsgründen über sein Elternteil wissen. – ng5000

+0

Generica wäre nicht geeignet für die eigentliche Problemdomäne, die ich modelliere. Dies sind bestimmte Typen und Beziehungen. – ng5000

+0

Danke für das Update - Ich werde Schnittstellen hinzufügen. Ich denke, der allgemeine Konsens darüber, dass an Zirkelverweisen eigentlich nichts falsch ist, ist der Hauptgrund für diese Frage. – ng5000

1

Da sehe ich kein Problem mit Ihrem Klassenmodell, aber Sie könnten leicht einen sauberen Schnitt machen. Stellen Sie Entity eine Schnittstelle IEntity implementieren und EntityContainer halten eine IList und wenn Sie einen ganz bestimmten Grund für die Verwendung von IList haben, sollten Sie IEnumerable betrachten, Es würde es für den Verbraucher der EntityClass, die Sie verwenden, einfach. Seit dem Übergeben eines beliebigen Feldes von IEntity oder eines linq-Ausdrucks ist die Auswahl von IEntitäten möglich.

+0

Aufruf Enumerable.ToList() auf einer Liste ist definitiv nicht frei, da es immer eine neue Liste erzeugt - also muss es eine Kopie machen, die O (N) ist. –

+0

@Pavel nein Es ist optimiert, um das Listenobjekt einfach zurückzugeben, wenn es in einer Liste aufgerufen wird –

+0

Sie sind einfach falsch. Öffnen Sie System.Core.dll mit ILSpy und schauen Sie sich die Implementierung von 'Enumerable.ToList' an. Es ist buchstäblich so einfach wie 'return new List()' plus eine Argument Nullprüfung. Dies ist durchaus beabsichtigt - die Entwickler von LINQ wollten nicht, dass Bibliotheks-Clients Objekte zu etwas herabstufen konnten, was der Bibliotheksautor nicht von ihnen erwartet hatte, und möglicherweise interne Daten mutieren ließen. Also werden alle 'To ...()' Methoden eine Kopie erstellen, auch wenn das Original bereits von diesem Typ ist. Im Gegensatz dazu gibt das 'As ...()' das ursprüngliche Objekt zurück. –

1

Die Verwendung zirkulärer referenzierter Objekte ist in meinem Buch in Ordnung, vorausgesetzt, Sie verwenden beim Entwerfen dieser Objekte eine Art Lazy-Loading-Muster.

z.B.

Sie wollen Zugang: Company.Employee Und in einem anderen Szenario: Mitarbeiter.Firma

Welche d.h. Company.Employee.Company.Employee usw.

eine kreisförmige Referenzvoll ist, wenn diese Eigenschaften nicht lazy z.B. belastetes sind Ein Unternehmensobjekt lädt immer seine Employee-Eigenschaft, und das Employee-Objekt lädt immer seine Company-Eigenschaft, dann wird es nicht zu gut funktionieren, wenn Sie eine Datenquelle einführen.

Verwandte Themen