鉴于以下两个LINQ样本,在什么时候确定了LINQ数据源?
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
IEnumerable<int> linqToOjects = numbers.Where(x => true);
XElement root = XElement.Load("PurchaseOrder.xml");
IEnumerable<XElement> linqToXML = root.Elements("Address").Where(x => true);
我的理解是用于查询这两个不同数据源的底层代码存在于LINQ方法生成的IEnumerable对象中。
我的问题是,在什么时候确定是否会生成使用Linq To Objects库或Linq To XML库的代码?
我认为用于查询这些数据源的底层代码(实际上用于查询数据的代码)存在于它们自己的库中,并且依赖于数据源进行调用。我查看https://referencesource.microsoft.com/来查看Where子句/扩展方法的代码,认为对所需提供程序的调用可能在那里,但它似乎是通用的。
如何确定进入IEnumerable的魔力?
答案 0 :(得分:6)
&#34;数据源&#34;立即确定。例如,在您的第一个示例中,Where
的返回值是一个实现IEnumerable<int>
(特别是Enumerable.WhereArrayIterator<int>
类)的对象,该对象依赖于numbers
对象(存储为字段)。并且第二个示例中Where
的返回值是一个可枚举的对象,它依赖于xml元素对象。因此,即使在开始枚举之前,结果可枚举也知道从哪里获取数据。
答案 1 :(得分:3)
我的问题是,在什么时候确定是否代码 将生成使用Linq To Objects库或Linq To XML库?
我认为没有代码生成。 LINQ只使用数据源enumerator。
您有一个实施IEnumerable
的班级公开枚举器,它支持对a的简单迭代 指定类型的集合。
因此,您可以使用方法GetEnumerator。
返回循环访问集合的枚举数。
所有LINQ都需要工作,enumerator。
在您的示例中,您使用Where LINQ扩展方法来应用某些过滤器。
IEnumerable<T> Where(this IEnumerable<T> source, Func<T, bool> predicate)
在实施中我们需要:
- 获取枚举器(source.GetEnumerator())
- 遍历集合并应用过滤器(谓词)
在Enumerable reference source中,您已经实现了方法Where。您可以看到他对数组(TSource [])和list(List)使用了一些特定的实现,但他对实现IEnumerable的所有其他类使用WhereEnumerableIterator。 所以没有代码生成,代码就在那里。
我认为您可以理解课程WhereEnumerableIterator的实施,您只需先了解如何实施IEnumerator。
Here你可以看到MoveNext的实现。他们调用 source.GetEnumerator(),然后他们遍历集合( enumerator.MoveNext())并应用过滤器(谓词(项目))。
public override bool MoveNext() {
switch (state) {
case 1:
enumerator = source.GetEnumerator();
state = 2;
goto case 2;
case 2:
while (enumerator.MoveNext()) {
TSource item = enumerator.Current;
if (predicate(item)) {
current = item;
return true;
}
}
Dispose();
break;
}
return false;
}
XContainer.GetElement使用yield关键字返回IEnumerable。
在语句中使用yield关键字时,表示该 方法,运算符或获取它的访问器是迭代器。 使用yield来定义迭代器不需要显式 额外的类(保存枚举状态的类,请参阅 IEnumerator实例化IEnumerable和 用于自定义集合类型的IEnumerator模式。
由于yield关键字的神奇之处,我们可以获得一个IEnumerable,我们可以枚举该集合。这是LINQ唯一需要的东西。