2008-12-02 8 views
25

Avez-vous un type par défaut que vous préférez utiliser dans vos transactions avec les résultats des requêtes LINQ? Par défaut, LINQ retournera un IEnumerable<> ou peut-être un IOrderedEnumerable<>. Nous avons trouvé qu'un List<> est généralement plus utile pour nous, alors nous avons adopté l'habitude de ToList() ing nos requêtes la plupart du temps, et certainement en utilisant List<> dans nos arguments de fonction et les valeurs de retour.Avez-vous ToList()?

La seule exception à cela a été dans LINQ to SQL où l'appel .ToList() énumérerait prématurément le IEnumerable.

Nous utilisons également intensivement WCF, le type de collection par défaut est System.Array. Nous modifions toujours cette valeur à System.Collections.Generic.List dans la boîte de dialogue Paramètres de référence du service dans VS2008 pour des raisons de cohérence avec le reste de notre base de code.

Que faites-vous?

Répondre

23

ToListtoujours évalue immédiatement la séquence - pas seulement dans LINQ to SQL. Si vous voulez ça, c'est bien, mais ce n'est pas toujours approprié.

Personnellement, j'essaierais d'éviter de déclarer que vous renvoyez List<T> directement - habituellement IList<T> est plus approprié, et vous permet de passer à une implémentation différente plus tard. Bien sûr, il y a des opérations qui ne sont spécifiées que sur List<T> lui-même ... ce genre de décision est toujours délicat.

EDIT: (Je l'aurais mis dans un commentaire, mais ce serait trop volumineux.) L'exécution différée vous permet de traiter des sources de données qui sont trop grandes pour tenir dans la mémoire. Par exemple, si vous traitez des fichiers journaux - les transformant d'un format à un autre, les téléchargeant dans une base de données, développant des statistiques ou quelque chose comme ça - vous pouvez très bien gérer des quantités arbitraires de données , mais vous vraiment ne veulent pas tout aspirer en mémoire. Cela peut ne pas être une préoccupation pour votre application particulière, mais c'est quelque chose à garder à l'esprit.

+1

D'accord que ToList évalue immédiatement. Nous pensons que dans LINQtoSQL cela aura probablement un impact sur les performances (surtout si nous enchaînons quelques expressions LINQ), mais quand nous serons en mémoire, tout impact sur les performances sera négligeable - et la cohérence pour les humains est plus important. –

+3

Il existe des différences significatives au-delà des performances. En particulier, si quelque chose à propos de la requête (par exemple les données dans la source) change, alors l'exécution différée vous donnera une réponse différente. Parfois, c'est ce que vous voulez, parfois ce n'est pas le cas. –

2

Cela dépend si vous devez modifier la collection. J'aime utiliser un tableau quand je sais que personne ne va ajouter/supprimer des éléments. J'utilise une liste quand j'ai besoin de trier/ajouter/supprimer des éléments. Mais, habituellement, je le laisse comme IEnumerable aussi longtemps que je peux.

16

Nous avons le même scénario - les communications WCF à un serveur, le serveur utilise LINQtoSQL.

Nous utilisons .ToArray() lors de la demande d'objets du serveur, car il est "illégal" pour le client de changer la liste. (Ce qui signifie qu'il n'y a pas de raison de supporter ".Add", ".Remove", etc).

Cependant, toujours sur le serveur, je vous recommande de le laisser par défaut (ce qui n'est pas IEnumerable, mais plutôt IQueryable). De cette façon, si vous voulez filtrer encore plus en fonction de certains critères, le filtrage est STILL du côté SQL jusqu'à évaluation.

Ceci est un point très important car cela signifie des gains de performance incroyables ou des pertes en fonction de ce que vous faites.

Exemple:

// This is just an example... imagine this is on the server only. It's the 
// basic method that gets the list of clients. 
private IEnumerable<Client> GetClients() 
{ 
    var result = MyDataContext.Clients; 

    return result.AsEnumerable(); 
} 

// This method here is actually called by the user... 
public Client[] GetClientsForLoggedInUser() 
{ 
    var clients = GetClients().Where(client=> client.Owner == currentUser); 

    return clients.ToArray(); 
} 

Voyez-vous ce qui se passe là-bas?La méthode "GetClients" va forcer le téléchargement de TOUS les 'clients' depuis la base de données ... ALORS la clause Where arrivera dans la méthode GetClientsForLoogedInUser pour la filtrer.

Maintenant, remarquez le léger changement:

private IQueryable<Client> GetClients() 
{ 
    var result = MyDataContext.Clients; 

    return result.AsQueryable(); 
} 

Maintenant, l'évaluation réelle ne se produira pas jusqu'à ce que « .ToArray » est appelé ... et SQL fera le filtrage. BEAUCOUP mieux!

+0

Votre point est très différent. La plupart des gens semblent manquer quand même. Je vois de très mauvais exemples comme celui-ci utilisés tout le temps. Les gens n'arrêtent pas de penser à ce que cette distribution va faire à l'exécution. –

+0

Bonne réponse, trop claire. Merci beaucoup – MegaMind

7

Dans le cas Linq-to-Objects, renvoyer List<T> à partir d'une fonction n'est pas aussi agréable que de renvoyer IList<T>, comme le souligne THE VENERABLE SKEET. Mais souvent vous pouvez toujours faire mieux que cela. Si la chose que vous retournez doit être immuable, IList est un mauvais choix parce qu'elle invite l'appelant à ajouter ou supprimer des choses. Par exemple, vous avez parfois une méthode ou une propriété qui renvoie le résultat d'une requête Linq ou utilise yield return pour générer paresseusement une liste, puis vous réalisez qu'il est préférable de le faire la première fois que vous êtes appelé , cache le résultat dans un List<T> et renvoie la version mise en cache par la suite. C'est alors que renvoyer IList peut être une mauvaise idée, car l'appelant peut modifier la liste à ses propres fins, ce qui corrompra alors votre cache, rendant ses changements visibles pour tous les autres appelants.

Mieux vaut renvoyer IEnumerable<T>, donc tout ce qu'ils ont est l'itération vers l'avant. Et si l'appelant veut un accès aléatoire rapide, c'est-à-dire qu'il souhaite utiliser [] pour accéder par index, il peut utiliser ElementAt, que Linq définit pour qu'il renifle tranquillement IList et l'utilise s'il est disponible, sinon il fait l'idiot recherche linéaire.

Une chose que j'ai utilisée ToList est quand j'ai un système complexe d'expressions Linq mélangé avec des opérateurs personnalisés qui utilisent yield return pour filtrer ou transformer des listes. Passer en revue dans le débogueur peut devenir très déroutant car il saute autour de faire l'évaluation paresseuse, donc j'ajoute parfois temporairement un ToList() à quelques endroits pour que je puisse suivre plus facilement le chemin d'exécution. (Bien que si les choses que vous exécutons ont des effets secondaires, cela peut changer le sens du programme.)

+0

Sachez également qu'il existe un mouvement dans .NET 4.5 pour fournir réellement la version en lecture seule des interfaces populaires que nous avons appris à aimer telles que IList avec la nouvelle IReadOnlyList. http://msdn.microsoft.com/en-us/library/hh192385.aspx – jpierson

2

Si vous ne avez pas besoin des fonctionnalités supplémentaires de la liste <>, pourquoi ne pas simplement coller avec IQueryable <>? !?!?! Le plus petit dénominateur commun est la meilleure solution (surtout quand vous voyez la réponse de Timothy).

Questions connexes