对于需要引用其他类的类,C#中的优秀设计模式是什么?

我正在研究C#.NET中的业务问题。 我有两个名为C和W的类,它们将在不同的时间独立实例化。

类C的对象需要包含对类W的0 … n个对象的引用,即C对象最多可包含n个W对象。

每个W对象需要包含对C类的1个对象的引用,即W对象包含在一个C对象中。

通常首先实例化C类的对象。 稍后,将发现并实例化其W内容。 在此之后,我需要将C和W对象相互交叉引用。

这有什么好的设计模式? 我实际上有一些案例,我有三个或四个类,但我们可以谈论两个类,以保持简单。

我在考虑一些简单的事情:

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

这将暂时起作用,但我可以预见必须编写相当数量的代码来跟踪所有引用及其有效性。 我想在后面实现代码,只对容器进行浅刷新,并对所有引用类进行深度刷新。 还有其他方法,它们有什么优势?

编辑11/3:感谢所有人的好答案和良好的讨论。 我最终选择了jop的答案,因为它最接近我想要的,但其他答案也有帮助。 再次感谢!

如果您有Martin Fowler的重构书,请按照“将单向关联更改为双向”重构。

如果你没有它,这里是你的类在重构后的样子:

 class C { // Don't to expose this publicly so that // no one can get behind your back and change // anything private List 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); } } } 

请注意,我已将List设为私有。 通过枚举器公开Ws列表,而不是直接公开列表。

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

上面的代码正确处理所有权的转移。 假设您有两个C – C1和C2实例 – 以及W – W1和W2的实例。

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

在上面的代码中,C1包含W1,C2包含W2。 如果你将W2重新分配给C1

 W2.Container = C1; 

然后C2将有零项目,C1将有两个项目 – W1和W2。 你可以有一个漂浮的W.

 W2.Container = null; 

在这种情况下,W2将从C1的列表中删除,它将没有容器。 您还可以使用C中的Add和Remove方法来操作W的容器 – 因此C1.Add(W2)将自动从其原始容器中删除W2并将其添加到新容器中。

我通常会这样做:

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

因此,您的Contents属性是只读的,您只能通过聚合方法添加项目。

嗯,看起来你几乎得到它,有一个小故障 – 你必须能够控制C内列表的添加。

例如,

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

为了检查,你只需要遍历你的列表,我想:

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

不确定这是否是你需要的。

要意识到可能有多个W的实例具有相同的值但可能具有不同的C容器。

扩展Jons答案….

如果W不应该保持C存活,则可能需要弱引用。

另外……如果要转让所有权,添加应该更复杂……

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

一种选择是实现System.ComponentModel下的IContainer和IComponent接口。 C是容器,W是组件。 然后ComponentCollection类将用作W实例的存储,而IComponent.Site将提供C的反向链接。

这是我使用的模式。

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

显然,我有类BidiParentBidiChildList ,后者实现IList等。幕后更新是通过内部字段完成的,而更新来自使用的代码此域模型是通过公共属性完成的。