c# - 分层对象和 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.)
}

这样根标签(用户希望能够有很多独立的树)没有父标签,而非根标签必须有父标签。

  1. 这是实现层次结构的好方法吗?我找到了 Composite Pattern,但在我的领域中,所有标签都是简单的标签,对于领域专家来说,父标签和子标签之间没有区别。

  2. 在测试中使用 AutoFixture 时出现问题;当我需要创建一个简单的标签时,它会引发此错误:

    Failure: Ploeh.AutoFixture.ObjectCreationException: AutoFixture was unable to create an instance of type Ploeh.AutoFixture.Kernel.SeededRequest because the traversed object graph contains a circular reference.

编辑: 我读了Creating recursive tree with AutoFixture但这是不同的情况:我只有一个类,而不是 2 个,我不希望 autofixture 创建一棵树,但只有一个节点

最佳答案

Is this a good way to implement hierarchy?

我看到它有三个问题,一个轻微的,一个稍微严重一点的,还有一个在你的具体情况下明显有问题。

潜在问题:

1. 让我们从小问题开始,这是关于属性名称与其类型之间的关系。我建议名为 ParentTag 的属性本身应该是 Tag 类型。您将其声明为 int(就像您为 Id 所做的那样)这一事实表明您应该改为调用属性 ParentTagId ... 或者您更改Tag 的属性类型。

2. 现在来谈谈更严重的问题。我认为 Desc 指向一个直接的子标签。 (如果一个标签可以有多个子标签,您显然为此属性选择了错误的类型。您需要某种集合。但这是另一个问题。)

如果您不注意,同时存储父链接和子链接很容易导致不一致。因此,最好不要为每个标签都设置双向链接,而只存储一个方向的链接。

然而,这将使沿相反方向遍历层次结构变得复杂。解决这个问题的一种方法是只存储子链接;如果你想找到 T 的父标签,你首先要通过从根标签开始递归遍历层次结构来找到 T 并不断跟踪“路径” “你正在服用;父级将是路径中的倒数第二个标签。

3. 现在讨论最紧迫的问题。异常提示:

Ploeh.AutoFixture.ObjectCreationException […] because the traversed object graph contains a circular reference.

使用您当前的 Tag 实现,可以构建包含循环的标记层次结构。我想您不希望这样。

例如,标签 C 可以将 P 作为其父标签,尽管 P 已经是 C< 的子标签/em>。因此,如果您开始遵循从 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# - 分层对象和 AutoFixture,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24323300/

相关文章:

c# - 什么是 "csci"( Paypal )

c# - VS 2010 中与命名空间相关的错误

hierarchy - Web开发人员部门要实现的Web开发人员指定层次结构

javascript - 如何从平面 JavaScript 数组创建分层 HTML 结构?

javascript、循环引用和内存泄漏

c# - 在 C# 中解析 XML 文件

c# - 将更具体的泛型类型分配给不太具体的类型

java - 将 Java 中的路径排序为层次结构

php - 关于 preFlush 的循环引用

postgresql - 删除循环数据库关系