Question IEnumerable vs List - Que utiliser? Comment travaillent-ils?


J'ai quelques doutes sur le fonctionnement des énumérateurs et sur LINQ. Considérez ces deux sélections simples:

List<Animal> sel = (from animal in Animals 
                    join race in Species
                    on animal.SpeciesKey equals race.SpeciesKey
                    select animal).Distinct().ToList();

ou

IEnumerable<Animal> sel = (from animal in Animals 
                           join race in Species
                           on animal.SpeciesKey equals race.SpeciesKey
                           select animal).Distinct();

J'ai changé les noms de mes objets originaux pour que cela ressemble à un exemple plus générique. La requête elle-même n'est pas si importante. Ce que je veux demander est ceci:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. J'ai remarqué que si j'utilise IEnumerable, quand je débogue et inspecte "sel", qui dans ce cas est le IEnumerable, il a quelques membres intéressants: "inner", "outer", "innerKeySelector" et "outerKeySelector", ces 2 derniers semblent être des délégués. Le membre "interne" n'a pas d'instances "Animal", mais plutôt des instances "Species", ce qui était très étrange pour moi. Le membre "externe" contient des instances "Animal". Je présume que les deux délégués déterminent ce qui entre et ce qui en sort?

  2. J'ai remarqué que si j'utilise "Distinct", le "inner" contient 6 items (c'est incorrect car seulement 2 sont Distinct), mais le "outer" contient les valeurs correctes. Encore une fois, probablement les méthodes déléguées déterminent cela mais c'est un peu plus que je ne sais sur IEnumerable.

  3. Plus important encore, laquelle des deux options est la plus performante?

La mauvaise liste de conversion via .ToList()?

Ou peut-être utiliser l'énumérateur directement?

Si vous le pouvez, veuillez aussi expliquer un peu ou jeter quelques liens qui expliquent cette utilisation de IEnumerable.


534
2017-09-02 15:05


origine


Réponses:


IEnumerable décrit le comportement, tandis que List est une implémentation de ce comportement. Lorsque vous utilisez IEnumerable, vous donnez au compilateur une chance de reporter le travail à plus tard, optimisant peut-être en cours de route. Si vous utilisez ToList () vous forcez le compilateur à réifier les résultats immédiatement.

Chaque fois que je "empile" des expressions LINQ, j'utilise IEnumerable, car en spécifiant uniquement le comportement, je donne à LINQ une chance de différer l'évaluation et éventuellement d'optimiser le programme. Vous souvenez-vous comment LINQ ne génère pas le SQL pour interroger la base de données jusqu'à ce que vous l'énumériez? Considère ceci:

public IEnumerable<Animals> AllSpotted()
{
    return from a in Zoo.Animals
           where a.coat.HasSpots == true
           select a;
}

public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Felidae"
           select a;
}

public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Canidae"
           select a;
}

Maintenant, vous avez une méthode qui sélectionne un échantillon initial ("AllSpotted"), plus quelques filtres. Alors maintenant vous pouvez faire ceci:

var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());

Il est donc plus rapide d'utiliser List sur IEnumerable? Seulement si vous voulez empêcher une requête d'être exécutée plus d'une fois. Mais est-ce mieux dans l'ensemble? Bien dans ce qui précède, Léopards et Hyènes se convertissent en requêtes SQL simples chaqueet la base de données renvoie uniquement les lignes pertinentes. Mais si nous avions renvoyé une liste de AllSpotted(), alors il peut courir plus lentement parce que la base de données pourrait renvoyer beaucoup plus de données que réellement nécessaire, et nous gaspillons des cycles faisant le filtrage dans le client.

Dans un programme, il peut être préférable de reporter la conversion de votre requête à une liste jusqu'à la fin, donc si je vais énumérer plus d'une fois les Léopards et les Hyènes, je ferais ceci:

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();

593
2017-09-02 15:36



Une classe qui met en œuvre IEnumerable vous permet d'utiliser le foreach syntaxe.

Fondamentalement, il a une méthode pour obtenir l'élément suivant dans la collection. Il n'a pas besoin que toute la collection soit en mémoire et ne connaisse pas le nombre d'objets qui s'y trouvent, foreachcontinue à obtenir l'élément suivant jusqu'à ce qu'il soit épuisé.

Cela peut être très utile dans certaines circonstances, par exemple dans une table de base de données massive, vous ne voulez pas copier l'ensemble de la chose en mémoire avant de commencer à traiter les lignes.

À présent List met en oeuvre IEnumerable, mais représente l'ensemble de la collection en mémoire. Si vous avez un IEnumerable et vous appelez .ToList() vous créez une nouvelle liste avec le contenu de l'énumération en mémoire.

Votre expression linq renvoie une énumération, et par défaut l'expression s'exécute lorsque vous effectuez une itération en utilisant le foreach. Un IEnumerable instruction linq s'exécute lorsque vous itérez le foreach, mais vous pouvez le forcer à itérer plus tôt en utilisant .ToList().

Voici ce que je veux dire:

var things = 
    from item in BigDatabaseCall()
    where ....
    select item;

// this will iterate through the entire linq statement:
int count = things.Count();

// this will stop after iterating the first one, but will execute the linq again
bool hasAnyRecs = things.Any();

// this will execute the linq statement *again*
foreach( var thing in things ) ...

// this will copy the results to a list in memory
var list = things.ToList()

// this won't iterate through again, the list knows how many items are in it
int count2 = list.Count();

// this won't execute the linq statement - we have it copied to the list
foreach( var thing in list ) ...

108
2017-09-02 15:24



Il y a un très bon article écrit par: TechBlog de Claudio Bernasconi ici: Quand utiliser IEnumerable, ICollection, IList et List

Voici quelques points de base sur les scénarios et les fonctions:

enter image description here enter image description here


99
2018-06-04 14:07



La chose la plus importante à réaliser est que, en utilisant Linq, la requête ne soit pas évaluée immédiatement. Il est seulement exécuté dans le cadre de l'itération à travers le résultat IEnumerable<T> dans un foreach C'est ce que font tous les délégués bizarres.

Ainsi, le premier exemple évalue la requête immédiatement en appelant ToList et en mettant les résultats de la requête dans une liste.
Le deuxième exemple renvoie un IEnumerable<T> qui contient toutes les informations nécessaires pour exécuter la requête plus tard.

En termes de performance, la réponse est ça dépend. Si vous avez besoin que les résultats soient évalués immédiatement (disons, vous mutez les structures que vous interrogez plus tard, ou si vous ne voulez pas l'itération sur le IEnumerable<T> prendre beaucoup de temps) utiliser une liste. Sinon, utilisez un IEnumerable<T>. La valeur par défaut devrait être d'utiliser l'évaluation à la demande dans le second exemple, car cela nécessite généralement moins de mémoire, à moins qu'il n'y ait une raison spécifique de stocker les résultats dans une liste.


64
2017-09-02 15:08



Personne n'a mentionné une différence cruciale, ironiquement répondu sur une question fermée comme une copie de cela.

IEnumerable est en lecture seule et List ne l'est pas.

Voir Différence pratique entre List et IEnumerable


60
2017-12-09 08:30



L'avantage de IEnumerable est une exécution différée (généralement avec des bases de données). La requête ne sera pas exécutée tant que vous n'aurez pas bouclé les données. C'est une requête qui attend jusqu'à ce que ce soit nécessaire (aka chargement paresseux).

Si vous appelez ToList, la requête sera exécutée, ou "matérialisée" comme j'aime le dire.

Il y a des avantages et des inconvénients pour les deux. Si vous appelez ToList, vous pouvez supprimer un certain mystère quant au moment où la requête est exécutée. Si vous vous en tenez à IEnumerable, vous avez l'avantage que le programme ne fonctionne pas tant qu'il n'est pas réellement requis.


34
2017-09-02 15:13



Si tout ce que vous voulez faire est de les énumérer, utilisez le IEnumerable.

Méfiez-vous, cependant, que le changement de la collection originale étant énuméré est une opération dangereuse - dans ce cas, vous voudrez ToList premier. Cela va créer un nouvel élément de liste pour chaque élément en mémoire, en énumérant les IEnumerableet est donc moins performant si vous n'énumérez qu'une seule fois - mais plus sûr et parfois le List les méthodes sont pratiques (par exemple en accès aléatoire).


15
2017-09-02 15:09



Je vais partager un concept mal utilisé que je suis tombé en un jour:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));


// updating existing list
names[0] = "ford";

// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

Résultat attendu

// I was expecting    
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari

Résultat actuel

// what printed actualy   
print( startingWith_M.ToList() ); // mazda
print( startingWith_F.ToList() ); // ford, fiat, ferrari

Explication

Comme pour les autres réponses, l'évaluation du résultat a été différée jusqu'à l'appel ToList ou des méthodes d'invocation similaires par exemple ToArray.

Je peux donc réécrire le code dans ce cas comme:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list
names[0] = "ford";

// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

Jouer aux alentours

https://repl.it/E8Ki/0


10
2017-11-26 08:03