在层次结构中获取实体的父级

时间:2010-11-16 16:35:01

标签: c# ienumerable linq-to-objects hierarchy

我试图在几天没有进展的情况下解决这个问题,我的问题是:

  

给定层次结构中的实体,返回   一个直系列表的列表(或   实体的父 s )。

当我想知道“实体1.2.2.2”的层次结构时,它将返回一个仅包含粗体项的列表:

Entity 1
- Entity 1.1
- Entity 1.2
  - Entity 1.2.1
  - Entity 1.2.2
    - Entity 1.2.2.1
    - Entity 1.2.2.2
  - Entity 1.2.3
  - Entity 1.2.4
    - Entity 1.2.4.1
- Entity 1.3
- Entity 1.4
Entity 2
...

因此,预期结果:

Entity 1
- Entity 1.2
  - Entity 1.2.2
    - Entity 1.2.2.2

实施代码:

class Entity
{
    public Entity Parent { get; set; }

    public bool AbsoluteParent 
    { 
        get
        { 
            return Parent == null;
        } 
    }

    public IEnumerable<Entity> Hierarchy //problem
    { 
        get
        {
            return AbsoluteParent 
                ? new [] { this }
                : new [] { this }.Concat( Parent.Hierarchy );
        }
    }

}

上面的数组尝试只是我一直在尝试的选项之一,该代码实际上返回的内容如下:

Entity 1
Entity 1
- Entity 1.2
Entity 1
- Entity 1.2
  - Entity 1.2.2
Entity 1
- Entity 1.2
  - Entity 1.2.2
    - Entity 1.2.2.2

我能够使用jQuery的parents()函数实现预期的结果,我一直在阅读yield return关键字,但仍然坚持其更多(我猜)功能风格,我还是宝贝 - 进入。

3 个答案:

答案 0 :(得分:4)

迭代器块是否可以接受?

public IEnumerable<Entity> Hierarchy
{
    // note: buffers results
    get { return UpwardHierarchy.Reverse(); }    
}

public IEnumerable<Entity> UpwardHierarchy
{
    get
    {
        // genuine lazy streaming
        for (var entity = this; entity != null; entity = entity.Parent)
            yield return entity;
    }
}

另一种选择:使用MoreLinq's Generate

return MoreEnumerable.Generate(this, entity => entity.Parent)
                     .TakeWhile(entity => entity != null)
                     .Reverse();

答案 1 :(得分:2)

当@Ani对他的回答发表评论时,无法从根本上获得一个懒惰的层次结构。

有一个有趣的manner of execution table将“Reverse()”方法显示为“延迟非流式执行”。所以它只在你访问它的第一个项目(root)时进行评估,但它需要读取整个UpwardHierarchy以产生第一个项目。

如果你想要表现,那么你应该注意的是linq(以及任何“yield return”方法)产生IEnumerable&lt;&gt;的方式。如果你要在Hierarchy上只迭代一次,那么可以忽略这个警告,但如果要多次搜索Hierarchy项,最好调用ToList()以避免重新处理UpwardHierarchy和“Reverse()” ”

一些代码:

[TestClass]
public class HierarchyTest
{
    #region Ani's code
    class Entity
    {
        public Entity Parent { get; set; }

        public IEnumerable<Entity> Hierarchy
        {
            // note: buffers results
            get { return UpwardHierarchy.Reverse(); }
        }

        public int YieldCount = 0;//modified
        public IEnumerable<Entity> UpwardHierarchy
        {
            get
            {
                // genuine lazy streaming
                for (var entity = this; entity != null; entity = entity.Parent)
                {
                    YieldCount++;//modified
                    yield return entity;
                }
            }
        }
    }
    #endregion


    [TestMethod]
    public void TestMethod1()
    {
        /*
        Entity 1
        - Entity 1.2
          - Entity 1.2.2
            - Entity 1.2.2.2
         */
        var e1 = new Entity();
        var e12 = new Entity() { Parent = e1 };
        var e122 = new Entity() { Parent = e12 };
        var e1222 = new Entity() { Parent = e122 };

        var hierarchy = e1222.Hierarchy;
        Assert.AreEqual(0, e1222.YieldCount);//nothing was evaluated until now
        hierarchy.First();
        Assert.AreEqual(4, e1222.YieldCount);//the entire UpwardHierarchy has been yielded to get the first Hierarchy item
        hierarchy.First();
        Assert.AreEqual(8, e1222.YieldCount);//yielded all UpwardHierarchy  itens again to get the first Hierarchy item

        List<Entity> evaluatedHierarchy = e1222.Hierarchy.ToList();//calling ToList() produces a List<Entity> instance so UpwardHierarchy and Reverse() are evaluated only once
        Assert.AreEqual(12, e1222.YieldCount);//Yieldcount+=4 because of ToList()
        evaluatedHierarchy.First();
        Assert.AreEqual(12, e1222.YieldCount);//and now you can use evaluatedHierarchy as you wish without causing another UpwardHierarchy and Reverse() call.
        evaluatedHierarchy.First();
        Assert.AreEqual(12, e1222.YieldCount);
    }
}

答案 2 :(得分:1)

这不是一种非常LINQ的方式(我的LINQ仍然很新):

List<Entity> graph = new List<Entity>()
Entity current = this;

while (current != null) {
   graph.Add(current);
   current = current.Parent;
}

graph.Reverse();

return graph;