LINQ是执行广度优先还是深度优先?
例如,请考虑以下代码段:
var stories = syndicationFeed.Items
.Select ( item => ConstructStory ( item ) )
.Where ( story => story != null )
.OrderByDescending ( story => story.WhenPublished )
.ToList();
在应用where子句之前是否完整执行了select子句(对于order子句也是如此)?或者是在尝试计算下一个对象之前计算的最终列表中的第一个对象?
答案 0 :(得分:2)
“在应用where子句之前,是否完整执行了select子句”
如果您正在使用LinqToEntites(IQueyable
个对象),则由驱动程序与数据源进行通信以确定顺序。
如果您正在使用LinqToObjects,它会尽可能地管理流程,因此 不 在第一个对象之前对每个对象执行Select
检查过滤器后,在 选择评估期间检查过滤器 。您的代码大致相当于。
List<Story> YourCode(Feed syndicationFeed)
{
IEnumerable<Story> storiesAsIEnumrable = syndicationFeed.EquivlantFunctionUpToToList()
var stories = storiesAsIEnumrable.ToList(storiesAsIEnumrable);
return stories;
}
IEnumerable<Story> EquivlantFunctionUpToToList(Feed feed)
{
List<Story> storyBufferForSorting = new List<Story>();
foreach(item in feed.Items)
{
var story = ConstructStory(item);
if(story != null)
{
storyBufferForSorting.Add(story);
}
}
storyBufferForSorting.OrderBy((x,y) => y.WhenPublished.CompareTo(x.WhenPublushed));
return storyBufferForSorting;
}
//Taken right from the .NET framework source code via ILSpy
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
return new List<TSource>(source);
}
所以是的,项目在排序之前得到完全枚举(它必须能够确定哪个项目是“第一个”),并且再次发生完整的枚举以在ToList()
中创建列表,但是过滤器在第一个枚举过程中发生,而不是额外的过程。
答案 1 :(得分:0)
如果syndicationFeed.Items
是IQueryable
,则将通过组合各种方法来构建查询,直到实际请求数据为止(在这种情况下,通过调用ToList()
)。
但是,这实际上取决于您查询的数据类型以及您希望对其执行的操作。
答案 2 :(得分:0)
我认为没有明确的答案(假设您谈到“Linq to Entities”)。我相信这取决于“Linq to Entities”驱动程序,该驱动程序在SQL查询中转换“Linq to Entities”查询。然后,它取决于SQL Server(或任何其他数据库服务器)处理特定查询的方式。
如果您真的非常关心性能,我建议您分析特定查询。使用SQL Server,您可以查看执行计划。也许这会回答你的问题。查看SQL server profiler和SQL Server execution planning。
希望我帮忙!
答案 3 :(得分:0)
你说你指的是LinqToObjects(使用Enumarble(of T)上的扩展方法)。
大多数LinqToObjects方法在实现中使用yield关键字,而Select是其中之一。 这意味着它不“选择”所有项目,然后继续进行下一个操作,它会逐个执行。
答案 4 :(得分:0)
Linq(对象)方法可以是流式传输或非流式传输。有一个完整的概述here。
大多数情况可以通过常识来理解。 Select
可以将其数据流式传输到后续的LINQ方法,因为它可以“触发并忘记”单个项目。另一方面,OrderBy
显然需要知道所有元素才能做任何事情。所以Select
是流媒体,OrderBy
不是。同样,Where
正在流式传输,因为它同样只需要一次处理一个元素。
您可以通过将trace语句放入作为LINQ方法参数的委托中来检查这一点。我做了一个简单的测试,我碰巧碰到了某些东西。在Linqpad:
Crops.Select (c => { ("s-" + c.CrpId).Dump(); return c.CrpId;})
.Where(i => { ("w1-" + i).Dump(); return i > 120; })
.Where(i => { ("w2-" + i).Dump(); return i > 150; })
.OrderBy (i => { ("o-" + i).Dump(); return i; })
如您所见,lambda表达式被方法体替换,这基本上没有任何区别。输出是:
s-21
w1-21
s-93
w1-93
...
s-122
w1-122
w2-122
s-123
w1-123
w2-123
...
s-158
w1-158
w2-158
o-155
o-157
o-158
因此,您会看到Where
和Select
已进行流式传输,OrderBy
会等待所有项目都可用。这也导致第二个Select
(这里有点做作,OK)只在必要时执行。