Ma question est la suivante : à quel moment exactement est-il déterminé si le code généré utilisera la bibliothèque Linq To Objects ou la bibliothèque Linq To XML ?
Je pense qu'il n'y a pas de génération de code. LINQ utilise simplement la source de données enumerator.
Vous avez une classe qui implémente IEnumerable
Expose l'énumérateur, qui prend en charge une simple itération sur une collection d'un type spécifié.
Ainsi, vous pouvez utiliser la méthode GetEnumerator.
Retourne un énumérateur qui itère à travers la collection.
Et c'est tout ce dont LINQ a besoin pour fonctionner, un enumerator.
Dans votre exemple, vous utilisez la méthode d'extension LINQ Where pour appliquer un filtre.
IEnumerable Where(this IEnumerable source, Func predicate)
Dans l'implémentation, nous devons :
- obtenir l'énumérateur (source.GetEnumerator())
- itérer à travers la collection et appliquer le filtre (predicate)
Dans la source de référence Enumerable, vous avez l'implémentation de la méthode Where. Vous pouvez voir qu'il utilise une implémentation spécifique pour les tableaux (TSource[]) et les listes (List), mais il utilise WhereEnumerableIterator pour toutes les autres classes qui implémentent IEnumerable. Il n'y a donc pas de génération de code, le code est là.
Je pense que vous pouvez comprendre l'implémentation de la classe WhereEnumerableIterator, vous avez juste besoin de comprendre comment implémenter d'abord IEnumerator.
Ici, vous pouvez voir l'implémentation de MoveNext. Ils appellent source.GetEnumerator() puis ils itèrent à travers la collection (enumerator.MoveNext()) et appliquent le filtre (predicate(item)).
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 retourne un IEnumerable en utilisant le mot-clé yield.
Lorsque vous utilisez le mot-clé yield dans une instruction, vous indiquez que la méthode, l'opérateur ou l'accesseur get dans lequel il apparaît est un itérateur. En utilisant yield pour définir un itérateur, vous supprimez la nécessité d'une classe supplémentaire explicite (la classe qui contient l'état pour une énumération, voir IEnumerator pour un exemple) lorsque vous mettez en œuvre le modèle IEnumerable et IEnumerator pour un type de collection personnalisé.
Grâce à la magie du mot-clé yield, nous pouvons obtenir un IEnumerable, et nous pouvons énumérer la collection. Et c'est la seule chose dont LINQ a besoin.