Pergunta

Atualmente estou tentando criar uma classe que implementa IEnumerable<T>, a fim de construir uma hierarquia de uma lista simples de objetos que têm referências entre si através de uma propriedade ParentId. Eu gostaria de escrever uma interface fluente para este para que eu possa fazer algo assim

IEnumerable<Tab> tabs = GetTabs();

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

Assim, sobre a declaração de rendimento, gostaria de saber se eu poderia fazer algo assim dentro de minha classe 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;
}

ou se eu teria que fazer algo como isto:

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
    };
}
Foi útil?

Solução

Não, select new não vai desencadear avaliação. Isto irá mapear para uma chamada para:

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

E nota que Select (para LINQ-to-Objects, pelo menos) é essencialmente algo como:

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

O ponto chave aqui é que ele avalia preguiçoso -. Não tudo de uma vez

abordagem Ou devem ser comparáveis ??- a única diferença é que, sem yield return, alguns código será executado imediatamente - mas apenas o código para construir a cadeia .Where(...).Select(...) - não vai realmente processar as linhas até que você iniciar a iteração o resultado.

Além disso, dependendo da fonte de dados, essa abordagem pode realmente ser mais eficiente -. Por exemplo, com um backend LINQ to SQL, como o gerador de TSQL pode pular as colunas desnecessárias

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top