3 votes

À quel moment un LINQ data source est-il déterminé?

Étant donné les deux exemples de LINQ ci-dessous, à quel moment une source de données LINQ est-elle déterminée?

int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
IEnumerable linqToOjects = numbers.Where(x => true);

XElement root = XElement.Load("PurchaseOrder.xml");
IEnumerable linqToXML = root.Elements("Address").Where(x => true);

Je pense que le code sous-jacent utilisé pour interroger ces deux sources de données différentes réside dans l'objet IEnumerable produit par les méthodes LINQ.

Ma question est, à quel moment exactement est-il déterminé si du code sera généré pour utiliser la bibliothèque Linq To Objects ou la bibliothèque Linq To XML?

Je présume que le code sous-jacent (le code qui effectivement réalise le travail d'interrogation des données) utilisé pour interroger ces sources de données existe au sein de leurs propres bibliothèques et est appelé en fonction de la source de données. J'ai consulté https://referencesource.microsoft.com/ pour examiner le code de la clause Where/méthode d'extension en pensant que l'appel au fournisseur désiré pourrait s'y trouver, mais il semble être générique.

Comment la magie qui opère dans l'objet IEnumerable est-elle déterminée?

6voto

Yacoub Massad Points 16759

La "source de données" est déterminée immédiatement. Par exemple, dans votre premier exemple, la valeur de retour de Where est un objet qui implémente IEnumerable (la classe Enumerable.WhereArrayIterator en particulier) qui dépend de l'objet numbers (stocké en tant que champ). Et la valeur de retour de Where dans le deuxième exemple est un objet énumérable qui a une dépendance sur l'objet élément XML. Ainsi, même avant de commencer à énumérer, l'énumérable résultant sait d'où obtenir les données.

3voto

Pedro Perez Points 490

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X