1

我目前正在尝试创建一个实现的类,IEnumerable<T>以便从通过 ParentId 属性相互引用的对象的平面列表构造层次结构。我想为此编写一个流畅的界面,这样我就可以做这样的事情

IEnumerable<Tab> tabs = GetTabs();

IEnumerable<TabNode> tabNodes = tabs.AsHierarchy().WithStartLevel(2).WithMaxDepth(5);

所以,关于 yield 语句,我想知道我是否可以在课堂上做这样的事情NodeHierarchy : IEnumerable<TabNode>

private IEnumerable<TabNode> _nodes;

public NodeHierarchy(IEnumerable<Tab> tabs)
{
    _nodes = CreateHierarchy(tabs);
}

public IEnumerable<TabNode> CreateHierarchy(IEnumerable<Tab> tabs)
{
    /* About this block: I'm trying to find the top level 
    nodes of the first tab collection, maybe this is done poorly? */
    var tabIds = tabs.Select(t => t.TabID);
    IEnumerable<TabNode> nodes = from tab in tabs
                             where !tabIds.Contains(tab.ParentId)
                                 select new TabNode {
                                            Tab = node,
                                            ChildNodes = CreateHierarchy(tabs, node.TabID, 1),
                                            Depth = 1 };
    return nodes;
}

或者我是否必须做这样的事情:

private IEnumerable<TabNode> _nodes;

public NodeHierarchy(IEnumerable<Tab> tabs)
{
    _nodes = CreateHierarchy(tabs);
}

public IEnumerable<TabNode> CreateHierarchy(IEnumerable<Tab> tabs)
{
var tabIds = tabs.Select(t => t.TabID);
IEnumerable<Tab> startingNodes = from tab in tabs
                                 where !tabIds.Contains(tab.ParentId)
                                 select tab;

foreach(Tab node in startingNodes)
{
    yield return
    new TabNode()
        {
        Tab = node,
        ChildNodes = CreateHierarchy(tabs, node.TabID, 1),
        Depth = 1
    };
}
4

1 回答 1

2

不,select new不会触发评估。这将映射到调用:

 .Select(tab => new TabNode {...})

请注意Select(至少对于 LINQ-to-Objects)本质上类似于:

public static IEnumerable<TDest> Select<TSource,TDest>(
    this IEnumerable<TSource> source,
    Func<TSource,TDest> selector)
{
    foreach(TSource item in source)
    {
        yield return selector(source);
    }
}

这里的关键点是它评估惰性 - 不是一次全部。

两种方法都应该具有可比性——唯一的区别是,如果没有yield return一些代码将立即运行——但只有构建.Where(...).Select(...)链的代码——它实际上不会处理行,直到你开始迭代结果。

此外,根据数据源的不同,这种方法实际上可能更有效——例如,使用 LINQ-to-SQL 后端,因为 TSQL 生成器可以跳过不必要的列。

于 2009-05-01T11:00:24.783 回答