Question

J'essaie actuellement de créer une classe qui implémente IEnumerable < T > afin de construire une hiérarchie à partir d'une liste plate d'objets se référant les uns aux autres via une propriété ParentId. Je voudrais écrire une interface fluide pour cela afin que je puisse faire quelque chose comme ça

IEnumerable<Tab> tabs = GetTabs();

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

Alors, à propos de la déclaration de rendement, je me demande si je pourrais faire quelque chose comme ceci dans ma hiérarchie de hiérarchie: 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 si je devrais faire quelque chose comme ceci:

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
    };
}
Était-ce utile?

La solution

Non, select new ne déclenchera pas d'évaluation. Cela correspond à un appel à:

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

Et notez que Select (pour LINQ-to-Objects, au moins) est essentiellement semblable à:

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

Le point clé ici est qu'il évalue le paresseux - pas tout à la fois.

Les deux approches doivent être comparables - la seule différence est que sans return return , du code sera exécuté immédiatement - mais seul le code permettant de générer le .Where (...). Sélectionnez la chaîne (...) - les lignes ne seront pas traitées tant que vous n'aurez pas commencé à itérer le résultat.

De plus, selon la source de données, cette approche peut en réalité être plus efficace - par exemple, avec un backend LINQ-to-SQL, car le générateur TSQL peut ignorer les colonnes inutiles.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top