¿"Seleccionar nuevo" en linq desencadena una evaluación / carga?
-
03-07-2019 - |
Pregunta
Actualmente estoy tratando de crear una clase que implemente IEnumerable < T >
para construir una Jerarquía a partir de una lista plana de objetos que tienen referencias entre sí a través de una propiedad ParentId. Me gustaría escribir una interfaz fluida para esto, así puedo hacer algo como esto
IEnumerable<Tab> tabs = GetTabs();
IEnumerable<TabNode> tabNodes = tabs.AsHierarchy().WithStartLevel(2).WithMaxDepth(5);
Entonces, sobre la declaración de rendimiento, me pregunto si podría hacer algo como esto dentro de mi NodeHierarchy: IEnumerable < TabNode >
class:
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;
}
o si tendría que hacer algo como esto:
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
};
}
Solución
No, seleccionar nuevo
no activará la evaluación. Esto se asignará a una llamada a:
.Select(tab => new TabNode {...})
Y tenga en cuenta que Select
(para LINQ-to-Objects, al menos) es esencialmente 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);
}
}
El punto clave aquí es que evalúa la pereza, no todas a la vez.
Cualquiera de los dos enfoques debería ser comparable: la única diferencia es que sin return return
, some se ejecutará inmediatamente, pero solo el código para construir el . (...). Seleccione (...)
cadena: en realidad no procesará las filas hasta que comience a iterar el resultado.
Además, dependiendo de la fuente de datos, ese enfoque puede ser más eficiente, por ejemplo, con un backend LINQ-to-SQL, ya que el generador TSQL puede omitir las columnas innecesarias.