gt.*_*ush 5 c# hierarchy circular-reference autofixture
我已经实现了一个用于存储标记的类,标记集合必须是层次化的,所以我的类是:
public class Tag
{
public int Id { get; set; }
public int Description { get; set; }
public Tag ParentTag { get; set; }
// … (methods for get children, add and remove children, etc.)
}
Run Code Online (Sandbox Code Playgroud)
这样,根标签(用户希望能够拥有许多单独的树)没有父标记,而非根标记必须具有父标记.
这是实现层次结构的好方法吗?我发现复合模式,但在我的域中,所有标签都只是标签,对于域专家,父标签和子标签之间没有区别.
在测试中使用AutoFixture会出现问题; 当我需要创建一个简单的标签时,它会引发这个错误:
失败::
Ploeh.AutoFixture.ObjectCreationExceptionAutoFixture无法创建类型的实例,Ploeh.AutoFixture.Kernel.SeededRequest因为遍历的对象图包含循环引用.
编辑:我阅读使用AutoFixture创建递归树,但它是不同的情况:我只有一个类,而不是2,我不希望自动混合创建一个树,但只有一个节点
这是实现层次结构的好方法吗?
我看到它有三个问题,一个是小问题,一个是更严重一个问题,一个问题在你的具体情况中显然有问题.
潜在问题:
1.让我们开始与未成年人问题,这是关于属性的名称和类型之间的关系.我建议命名的属性ParentTag应该是Tag类型本身.您声明它的事实int(就像您所做的那样Id)表明您应该调用该属性ParentTagId...或者您将属性的类型更改为Tag.
2.现在到了更严重的问题.我认为它指的Desc是直接的儿童标签.(如果一个标签可以有多个子标签,你显然选择了这个属性的错误类型.你需要某种类型的集合.但这是另一个问题.)
如果您没有得到足够的重视,存储父级和子级链接很容易导致不一致.因此,最好不要为每个标签设置双向链路,而是仅存储一个方向的链路.
然而,这将使在相反方向上遍历层次结构变得复杂.解决这个问题的一种方法是只存储子链接; 如果你想找到T的父标签,你首先要通过递归遍历从根标签开始的层次结构找到T,并持续跟踪你正在采取的"路径"; 然后父节点将成为路径中的倒数第二个标签.
3.现在是最直接的问题.异常提示:
Ploeh.AutoFixture.ObjectCreationException[...]因为遍历的对象图包含循环引用.
使用当前的实现Tag,可以构建包含循环的标记层次结构.我假设你不想那样.
例如,标签C可以将P作为其父标签,尽管P已经是C的子标签.因此,如果你开始跟随ParentTag从C开始的链,你将首先到达P然后最终返回到C,如果你继续前进,你会发现自己陷入无限循环.
我不知道AutoFixture,但由于类似的原因,它似乎无法处理您的具体标签层次结构.
你应该使你的标签层次结构有向无环图(DAG) - 这里的"非循环"是重要的一点.但是,使用当前的Tag类,您可以构建任何有向图 ; 它不保证不会有任何循环.
防止循环标记层次结构的方法:
1.在ParentTagsetter中实现循环检查:
public Tag ParentTag
{
…
set
{
if (!IsOrIsAncestorOf(value))
{
parentTag = value;
}
else
{
throw new ArgumentException("ParentTag", "would cause a cycle");
}
}
}
private Tag parentTag;
private bool IsOrIsAncestorOf(Tag other)
{
return this == other || IsOrIsAncestorOf(other.Parent));
// ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Is … Or … IsAncestorOf
}
Run Code Online (Sandbox Code Playgroud)
2.甚至更简单,make ParentTag readonly,它会强制你在构造函数中设置它.这将自动使得无法构建循环标记层次结构 - 如果您不相信它,请尝试它:
public Tag(Tag parentTag)
{
this.parentTag = parentTag;
}
private readonly Tag parentTag;
public Tag ParentTag
{
get
{
return parentTag;
}
}
Run Code Online (Sandbox Code Playgroud)
我会推荐第二种解决方案.