C# 层次对象与自动夹具
我实现了一个用于存储标记的类,标记集合必须是分层的,因此我的类是:C# 层次对象与自动夹具,c#,hierarchy,circular-reference,autofixture,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.) } 这样,根标记(用户希望能够有许
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.)
}
这样,根标记(用户希望能够有许多独立的树)没有父标记,而非根标记必须有父标记
Ploeh.AutoFixture.ObjectCreationException
:AutoFixture无法创建类型为Ploeh.AutoFixture.Kernel.SeedRequest
的实例,因为遍历的对象图包含循环引用
ParentTag
的属性本身应为Tag
类型。您将其声明为int
(就像您对Id
所做的那样)这一事实表明,您应该调用属性ParentTagId
,或者将属性的类型更改为Tag
2.现在来谈谈更严重的问题。我认为Desc
指向一个立即子标记。(如果一个标记可以有多个子标记,那么显然您为此属性选择了错误的类型。您需要某种类型的集合。但这是另一个问题。)
如果您没有注意,同时存储父链接和子链接很容易导致不一致。因此,最好不要为每个标记提供双向链接,而只存储指向一个方向的链接
然而,这将使以相反方向遍历层次结构变得复杂。解决这个问题的一种方法是存储独子链接;如果你想找到T的父标记,你首先要递归地遍历从根标记开始的层次结构,并持续跟踪你所走的“路径”,从而找到T;然后,父项将是路径中倒数第二个标记
3.现在开始最直接的问题。例外情况提示:
Ploeh.AutoFixture.ObjectCreationException
[…],因为遍历的对象图包含循环引用
使用当前的标记实现
,可以构建包含循环的标记层次结构。我想你不会想要的
例如,标记C可以将p作为其父标记,尽管p已经是C的子标记。因此,如果您开始遵循从C开始的ParentTag
链,您将首先到达p,然后最终返回C,如果继续,您将发现自己陷入无限循环
我不知道AutoFixture,但由于类似的原因,它似乎无法处理具体的标记层次结构
你应该建立你的标签层次结构,“非循环”是这里的重要一点。但是,使用当前的标记
类,您可以构建任何;它不能保证不会有任何循环
防止循环标记层次结构的方法:
1。在ParentTag
setter中执行周期检查:
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
}
2.更简单的是,将ParentTag
设置为只读
,这将强制您在构造函数中设置它。这将自动使构建循环标记层次结构变得不可能-如果您不相信,请尝试:
public Tag(Tag parentTag)
{
this.parentTag = parentTag;
}
private readonly Tag parentTag;
public Tag ParentTag
{
get
{
return parentTag;
}
}
我建议使用第二种解决方案。对不起,我编辑类更改了ParentTag类型,int版本是错误的。Desc属性用于描述,编辑为“问题更新”以回答“复制我的标记”seemann@MarkSeemann你能移除重复的标签吗?谢谢,还有,在构造函数中添加一个参数,我再也不能创建一个假的Moqsolved了。添加一个空的构造函数来创建根标记,正如你所建议的,第二个解决方案是:简单而简洁地写“解决这个问题的一个方法是只存储子链接”,但这样我怎么能移动标记呢?即使现在,我也必须从private field中删除readonly属性,以便能够在Move()方法中更改它。我认为我们无法在评论部分解决这个问题。但基本上,您可以通过重建层次结构来移动标记。这听起来比实际情况更糟;毕竟,你可以“回收”未受影响的零件。