use*_*358 13 .net c# circular-dependency circular-reference
假设我在同一个程序集中有以下类
public class ParentClass : IDisposable
{
public ChildClass Child
{
get { return _child; }
}
}
public class ChildClass
{
public ParentClass Parent
{
get { return _parent; }
set { _parent= value; }
}
public ChildClass (ParentClass parent)
{
Parent= parent;
}
}
Run Code Online (Sandbox Code Playgroud)
如果我错了,请纠正我,但这是糟糕的设计.这会导致内存泄漏或其他一些不可预见的问题吗?显然垃圾收集器能够处理这种循环引用.
编辑
如果这两个类最终在其他类中被这样使用怎么办?
ParentClass objP = new ParentClass ();
ChildClass objC =new ChildClass(objP);
objP.Child = objC;
Run Code Online (Sandbox Code Playgroud)
想一想......
Eri*_*ert 18
不要担心垃圾收集器; 它可以轻松处理具有任意拓扑的参考图.担心编写可以通过简化违反其不变量来创建错误的对象.
这是一个值得怀疑的设计,不是因为它强调GC - 它没有 - 而是因为它没有强制执行所需的语义不变量:如果X是Y的父级,则Y必须是X的子级.
编写保持一致的父子关系的类可能非常棘手.我们在Roslyn团队所做的是我们实际上建造了两棵树."真正的"树只有子引用; 没有孩子知道它的父母.我们在其上层叠了一个"facade"树,它强制了父子关系的一致性:当你向父节点询问它的子节点时,它会在它的真实子节点之上创建一个外观并设置该外观对象的父节点.成为真正的父母.
更新:评论员布莱恩要求提供更多细节.下面是如何在仅包含子引用的"绿色"树上实现带有子引用和父引用的"红色"外观的草图.在这个系统中,不可能产生不一致的父子关系,你可以在底部的测试代码中看到.
(我们将这些称为"红色"和"绿色"树,因为在白板上绘制数据结构时,这些是我们使用的标记颜色.)
using System;
interface IValue { string Value { get; } }
interface IParent : IValue { IChild Child { get; } }
interface IChild : IValue { IParent Parent { get; } }
abstract class HasValue : IValue
{
private string value;
public HasValue(string value)
{
this.value = value;
}
public string Value { get { return value; } }
}
sealed class GreenChild : HasValue
{
public GreenChild(string value) : base(value) {}
}
sealed class GreenParent : HasValue
{
private GreenChild child;
public GreenChild Child { get { return child; } }
public GreenParent(string value, GreenChild child) : base(value)
{
this.child = child;
}
public IParent MakeFacade() { return new RedParent(this); }
private sealed class RedParent : IParent
{
private GreenParent greenParent;
private RedChild redChild;
public RedParent(GreenParent parent)
{
this.greenParent = parent;
this.redChild = new RedChild(this);
}
public IChild Child { get { return redChild; } }
public string Value { get { return greenParent.Value; } }
private sealed class RedChild : IChild
{
private RedParent redParent;
public RedChild(RedParent redParent)
{
this.redParent = redParent;
}
public IParent Parent { get { return redParent; } }
public string Value
{
get
{
return redParent.greenParent.Child.Value;
}
}
}
}
}
class P
{
public static void Main()
{
var greenChild1 = new GreenChild("child1");
var greenParent1 = new GreenParent("parent1", greenChild1);
var greenParent2 = new GreenParent("parent2", greenChild1);
var redParent1 = greenParent1.MakeFacade();
var redParent2 = greenParent2.MakeFacade();
Console.WriteLine(redParent1.Value); // parent1
Console.WriteLine(redParent1.Child.Parent.Value); // parent1 !
Console.WriteLine(redParent2.Value); // parent2
Console.WriteLine(redParent2.Child.Parent.Value); // parent2 !
// See how that goes? RedParent1 and RedParent2 disagree on what the
// parent of greenChild1 is, **but they are self-consistent**. They
// always report that the parent of their child is themselves.
}
}
Run Code Online (Sandbox Code Playgroud)
And*_*ker 11
从技术角度来看,它既不是特别糟糕的设计,也不是问题.类实例(对象)是引用类型,即_parent和_child仅保存对相应对象的引用,而不是对象本身.所以你不会导致一些无限的数据结构.
正如您自己发布的那样,垃圾收集器能够处理循环引用,主要是因为它不使用引用计数.
这种结构的唯一"问题"通常只是你经常想要保持关系的两端同步,即.给予Child c和Parent p p.Child == c if and only if c.Parent == p.您需要决定如何以最佳方式处理此问题.例如,如果你有一个方法Parent.AddChild(Child c),这不仅可以将Parent.Child设置为c,而且还可以将Child.Parent设置为父级.但是,如果直接分配Child.Parent呢?这些是您可能需要处理的一些问题.
| 归档时间: |
|
| 查看次数: |
1690 次 |
| 最近记录: |