我实现了一个用于存储标签的类,标签集合必须是分层的,所以我的类是:
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.)
}
这样,根标记(用户希望能够拥有许多单独的树)没有父标记,而非根标记必须具有父标记。编辑:
我读了Creating recursive tree with AutoFixture,但是情况不同:我只有一个类,没有2个类,并且我不希望自动固定装置创建树,而只希望创建一个节点
最佳答案
我看到了三个问题,一个是小问题,一个是更严重的问题,另外一个在您的具体情况下显然有问题。
潜在问题:
1. 让我们从一个较小的问题开始,该问题与属性名称及其类型之间的关系有关。我建议一个名为ParentTag
的属性本身应为Tag
类型。您将其声明为int
(就像对Id
所做的那样)的事实表明,您应该改为调用属性ParentTagId
…或将属性的类型更改为Tag
。
2. 现在到更严重的问题。我认为Desc
指向直接子标记。 (如果一个标签可以有多个子标签,则显然您为此属性选择了错误的类型。您需要某种收集。但这是另一个问题。)
如果您没有注意的话,同时存储 parent 和 child 的链接很容易导致不一致。因此,最好不要为每个标签都具有双向链接,而只存储一个方向上的链接。
但是,这将使在相反方向上遍历层次结构变得复杂。解决此问题的一种方法是仅存储子链接。如果您随后想要查找T的父标签,则首先要通过从根标签开始递归遍历层次结构并持续跟踪您要采用的“路径”来找到T。父对象将成为路径中的倒数第二个标签。
3. 现在到最直接的问题。异常提示了它:
使用当前的Tag
实现,可以构建包含循环的标记层次结构。我认为您不想要那样。
例如,标签C可以以P作为其父标签,尽管P已经是C的子标签。因此,如果您开始跟随以C开头的ParentTag
链,那么您将首先到达P,然后最终回到C,如果继续前进,您会发现自己陷入了无限循环。
我不知道AutoFixture,但由于类似的原因,它似乎无法处理您的具体标签层次结构。
您应该将标记层次结构设为directed acyclic graphs (DAGs)-“非循环”是此处的重要位。但是,使用当前的Tag
类,您可以构建任何directed graph;它不能保证不会有任何循环。
防止循环标记层次结构的方法:
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
设置为readonly
,这将迫使您在构造函数中进行设置。这将自动使其无法构建循环标记层次结构-如果您不相信,请尝试一下:public Tag(Tag parentTag)
{
this.parentTag = parentTag;
}
private readonly Tag parentTag;
public Tag ParentTag
{
get
{
return parentTag;
}
}
我建议第二种解决方案。关于c# - 分层对象和自动修复,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24323300/