Запускает ли “Выбрать новый” в linq оценку / загрузку?
-
03-07-2019 - |
Вопрос
В настоящее время я пытаюсь создать класс, который реализует 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
};
}
Решение
НЕТ, 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);
}
}
Ключевым моментом здесь является то, что он оценивает lazy - не все сразу.
Любой подход должен быть сопоставимым - разница лишь в том, что без yield return
, некоторые код будет запущен немедленно - но только код для создания .Where(...).Select(...)
цепочка - на самом деле она не будет обрабатывать строки, пока вы не начнете повторять результат.
Кроме того, в зависимости от источника данных, такой подход на самом деле может быть более эффективным - например, при использовании серверной части LINQ-to-SQL, поскольку генератор TSQL может пропускать ненужные столбцы.