对您的问题的回答取决于两个选项:
- 操作类型:懒惰(或延迟)和贪婪。延迟操作不会立即执行,而是延迟到代码开始从您的 linq 源实现数据。贪婪操作总是立即执行。
- 惰性操作示例:
.Union
, .Except
, .Where
, . Select
和大多数其他 linq 操作
- 贪心的是:
.ToList
,.Count
.ToArray
以及所有实现数据的操作
- linq 操作的数据源。当您使用 Linq to Memory 时,所有操作(懒惰和贪婪)都将立即执行。通常 Linq到外部数据源只会在物化期间执行惰性操作。
使用这两个规则,您可以预测 linq 的行为方式:
.Count
并将.ToList
立即执行并实现数据
- 之后
.ToList
,您将在内存中收集,并且将立即执行所有后续操作(.Count
将再次执行)
.Union
懒惰或贪婪的行为方式.Except
取决于数据源的类型。对于内存,他们将是贪婪的,对于 SQL 是懒惰的。
LinqPad的示例。在使用贪婪或实现之前和之后,我有一个Enumerable
懒惰或延迟.Where
和操作:.Select
.Count
.ToList
void Main()
{
"get enumerable".Dump();
var samplesEnumerable = GetSamples();
"get count on enumerable #1".Dump();
samplesEnumerable.Count().Dump();
"get enumerable to list #1".Dump();
var list = samplesEnumerable.ToList();
"get count on list #1".Dump();
list.Count().Dump();
"get count on list again #2".Dump();
list.Count().Dump();
"get where/select enumerable #1".Dump();
samplesEnumerable
.Where(sample => { sample.Dump(); return sample.Contains("5"); })
.Select(sample => { sample.Dump(); return sample; })
.Dump();
"get where/select list #1".Dump();
list
.Where(sample => { sample.Dump(); return sample.Contains("5"); })
.Select(sample => { sample.Dump(); return sample; })
.Dump();
}
string[] samples = new [] { "data1", "data2", "data3", "data4", "data5" };
IEnumerable<string> GetSamples()
{
foreach(var sample in samples)
{
sample.Dump();
yield return sample;
}
}
样本输出。关键点
在未具体化的数据上,每一次都在一次.Count
又一次.List
地检索数据
get count on enumerable #1
get where/select enumerable #1
物化数据后,将不再检索可枚举
get enumerable to list #1
get count on list #1
get count on list again #2
get where/select list #1
输出:
get enumerable
get count on enumerable #1
data1
data2
data3
data4
data5
5
get enumerable to list #1
data1
data2
data3
data4
data5
get count on list #1
5
get count on list again #2
5
get where/select enumerable #1
data1
data1
data2
data2
data3
data3
data4
data4
data5
data5
data5
data5
get where/select list #1
data1
data2
data3
data4
data5
data5
data5