What is a good design pattern in C # for classes that should reference other classes?

I am working on a business issue in C # .NET. I have two classes named C and W that will be created independently at different times.

An object of class C must contain references to 0 ... n objects of class W, that is, an object C can contain up to n objects of W.

Each object W must contain a reference to exactly one object of class C, i.e. object W is contained in one object C.

An object of class C is usually created first. At a later stage, its W-content is discovered and created. At this later stage, I need to cross-reference the objects C and W to each other.

What is a good design template for this? In fact, I have cases where I have three or four classes, but we can talk about two classes to make it simple.

I was thinking of something simple:

class C { public List<W> contentsW; } class W { public C containerC; } 

This will work for now, but I can anticipate writing enough code to keep track of all the links and their validity. I would like to implement the code along the way to make minor updates to just the container and deep updates to all the reference classes. Are there any other approaches and what are their advantages?

Change to 11/3: Thank you all for the good answers and good discussion. I finally chose the jop answer because it was closest to what I wanted to do, but other answers also helped. Thanks again!

+6
reference c # class class-design
source share
6 answers

If you have Martin Fowler's refactoring book, just follow the "Change Unidirectional Association to Bidirectional" refactoring.

If you don't have one, here is what your classes look like after refactoring:

 class C { // Don't to expose this publicly so that // no one can get behind your back and change // anything private List<W> contentsW; public void Add(W theW) { theW.Container = this; } public void Remove(W theW) { theW.Container = null; } #region Only to be used by W internal void RemoveW(W theW) { // do nothing if C does not contain W if (!contentsW.Contains(theW)) return; // or throw an exception if you consider this illegal contentsW.Remove(theW); } internal void AddW(W theW) { if (!contentW.Contains(theW)) contentsW.Add(theW); } #endregion } class W { private C containerC; public Container Container { get { return containerC; } set { if (containerC != null) containerC.RemoveW(this); containerC = value; if (containerC != null) containerC.AddW(this); } } } 

Note that I made List<W> private. List Ws through the counter instead of directly looking at the list.

eg. public List GetWs () {return this.ContentW.ToList (); }

The code above handles the correct transfer of ownership. Say you have two instances of C - C1 and C2 - and instances of W - W1 and W2.

 W1.Container = C1; W2.Container = C2; 

In the above code, C1 contains W1 and C2 contains W2. If you reassign W2 to C1

 W2.Container = C1; 

Then C2 will have zero elements, and C1 will have two elements - W1 and W2. You can have floating w

 W2.Container = null; 

In this case, W2 will be removed from the list C1, and there will be no container in it. You can also use the add and remove methods from C to manage W containers, so C1.Add (W2) will automatically remove W2 from the original container and add it to a new one.

+6
source share

Usually I do it something like this:

 class C { private List<W> _contents = new List<W>(); public IEnumerable<W> Contents { get { return _contents; } } public void Add(W item) { item.C = this; _contents.Add(item); } } 

So your content property is read-only, and you add items only through your aggregate method.

+3
source share

Hmmm, it looks like you almost got it, with one minor glitch - you should be able to control the list addition inside C.

eg.

 class C { private List<W> _contentsW; public List<W> Contents { get { return _contentsw; } } public void AddToContents(W content); { content.Container = this; _contentsW.Add(content); } } 

To check you just need to iterate over your list, I think:

 foreach (var w in _contentsW) { if (w.Container != this) { w.Container = this; } } 

Not sure what you need.

Understand that there may be multiple instances of W that will have the same value, but may have different containers C.

+2
source share

Extension on Jons Answer ....

You might need weak links if W doesn't have to keep alive.

Also ... adding should be more complicated if you want to transfer ownership ...

 public void AddToContents(W content); { if(content.Container!=null) content.Container.RemoveFromContents(content); content.Container = this; _contentsW.Add(content); } 
+1
source share

One option for this would be to implement IContainer and IComponent found in System.ComponentModel. C will be the container, and W the component. The ComponentCollection class will then serve as a repository for your W instances, and IComponent.Site will provide a reference back to C.

0
source share

This is the template I am using.

 public class Parent { public string Name { get; set; } public IList<Child> Children { get { return ChildrenBidi; } set { ChildrenBidi.Set(value); } } private BidiChildList<Child, Parent> ChildrenBidi { get { return BidiChildList.Create(this, p => p._Children, c => c._Parent, (c, p) => c._Parent = p); } } internal IList<Child> _Children = new List<Child>(); } public class Child { public string Name { get; set; } public Parent Parent { get { return ParentBidi.Get(); } set { ParentBidi.Set(value); } } private BidiParent<Child, Parent> ParentBidi { get { return BidiParent.Create(this, p => p._Children, () => _Parent, p => _Parent = p); } } internal Parent _Parent = null; } 

Obviously, I have the BidiParent<C, P> and BidiChildList<C, P> classes, the last of which implements IList<C> , etc. Behind the scenes, updates are done through internal fields, while updates from code that uses this domain model are done through public properties.

0
source share

All Articles