对于需要引用其他类的类,C#中什么是好的设计模式
本文关键字:什么 设计模式 引用 其他 于需 | 更新日期: 2023-09-27 17:47:46
我正在C#.NET中处理一个业务问题。我有两个类,分别命名为C和W,它们将在不同的时间独立实例化。
类C的对象需要包含对0的引用。。。n个W类对象,即一个C对象最多可以包含n个W对象。
每个W对象需要包含一个对C类的恰好一个对象的引用,即一个W对象包含在一个C对象中。
C类的对象通常首先被实例化。稍后,它的W内容被发现并实例化。在后面的这一点上,我需要将C和W对象相互交叉引用。
什么是好的设计模式?事实上,我有三到四个课程,但为了简单起见,我们可以讨论两个课程。
我在想一些简单的事情,比如:
class C
{
public List<W> 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<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);
}
}
}
请注意,我已将List<W>
设置为私有。通过枚举器公开W的列表,而不是直接公开列表。
例如,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<W> _contents = new List<W>();
public IEnumerable<W> Contents
{
get { return _contents; }
}
public void Add(W item)
{
item.C = this;
_contents.Add(item);
}
}
因此,Contents属性是只读的,并且只能通过聚合的方法添加项。
嗯,看起来你几乎做到了,但有一个小故障——你必须能够控制在C.中添加到列表中
例如
class C
{
private List<W> _contentsW;
public List<W> 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 Answer。。。。
如果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<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;
}
显然,我有类BidiParent<C, P>
和BidiChildList<C, P>
,后者实现了IList<C>
等。幕后更新是通过内部字段完成的,而使用此域模型的代码的更新是通过公共属性完成的。