Ist „Wählen Sie Neu“ in Linq auslösen ein Auswertungs- / Last?
-
03-07-2019 - |
Frage
Ich versuche zur Zeit eine Klasse zu erstellen, die IEnumerable<T>
um implementiert eine Hierarchie aus einer flachen Liste von Objekten zu konstruieren, die Verweise auf miteinander über eine ParentId Eigenschaft. Ich möchte eine fließend-Schnittstelle für diese schreiben, so dass ich etwas tun kann,
IEnumerable<Tab> tabs = GetTabs();
IEnumerable<TabNode> tabNodes = tabs.AsHierarchy().WithStartLevel(2).WithMaxDepth(5);
Also, um die Ausbeute Aussage, frage ich mich, ob ich so etwas wie dies in meiner NodeHierarchy : IEnumerable<TabNode>
Klasse tun konnte:
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;
}
oder ob ich hätte so etwas tun:
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
};
}
Lösung
Nein, select new
nicht Auswertung auslösen. Dies wird zu einem Aufruf der Karte an:
.Select(tab => new TabNode {...})
Und beachten Sie, dass Select
(für LINQ-to-Objects, zumindest) ist im Wesentlichen so etwas wie:
public static IEnumerable<TDest> Select<TSource,TDest>(
this IEnumerable<TSource> source,
Func<TSource,TDest> selector)
{
foreach(TSource item in source)
{
yield return selector(source);
}
}
Der entscheidende Punkt hier ist, dass es faul auswertet - nicht alle auf einmal
. Beide Ansätze sollten vergleichbar sein - der einzige Unterschied ist, dass ohne yield return
, einige Code sofort ausgeführt werden - aber nur der Code die .Where(...).Select(...)
Kette zu bauen - es wird nicht wirklich die Zeilen, bis Sie verarbeiten starten Sie das Ergebnis iterieren.
Darüber hinaus abhängig von der Datenquelle, kann dieser Ansatz tatsächlich effizienter sein - z. B. mit einem LINQ-to-SQL-Backend, da der TSQL Generator der nicht benötigten Spalten überspringt