2011-10-05 3 views
4

Basé sur le code suivant:LINQ: en utilisant IEnumerable.Count() ou IList.Count pour une meilleure performance

var grouped = filters.GroupBy(p => p.PropertyName); 
       int numOfRowElements = grouped.Count(); 
    foreach (IGrouping<string, PropertyFilter> filter in grouped) 
       { 


        foreach (var propertyFilter in filter) 
        { 
         // do something 
        } 

       } 

où filtre une liste, je crois comprendre que l'appel IEnumerable.Count() force la requête être exécuté. Le résultat de cette exécution est-il stocké dans la variable groupée, qui est ensuite utilisée dans la boucle foreach, ou la boucle foreach force-t-elle la requête à être exécutée à nouveau? Serait-il préférable de le faire à la place?

var grouped = filters.GroupBy(p => p.PropertyName).ToList(); 
    int numOfRowElements = grouped.Count; 
    foreach (IGrouping<string, PropertyFilter> filter in grouped) 
       { 


        foreach (var propertyFilter in filter) 
        { 
         // do something 
        } 

       } 

TIA.

Répondre

5

Si la source de données sous-jacente est IList<T>, Enumerable.Count() invoqueront la propriété .Count comme une optimisation, donc il n'y a pas * pénalité de performance. Si ce n'est pas le cas, une énumération sera forcée. Considérez ceci attentivement.

var someList = new List<int>(); 
var count = someList.Count(); // will use .Count property 
var count = someList.OrderBy(x => x).Count(); // will force enumeration 

Dans cet exemple, je viens d'obtenir le nombre de la liste dans la deuxième instruction. Dans le troisième, je commande la liste et ensuite je compte le nombre. La commande de la liste renvoie une séquence, pas une liste. Par conséquent, la méthode Count() est pas travaillant sur un IList<T>, mais un IEnumerable<T>. Dans ce cas, la requête doit être énumérée pour acquérir le résultat et engager le coût qui en résulte (dans ce cas, la commande). À la lumière de cela, dans votre premier extrait, vous allez énumérer votre requête deux fois. Une fois pour obtenir le compte, une fois dans la foreach. Cela va effectuer toute la logique pour regrouper vos données deux fois. Votre deuxième exemple effectuera les opérations de regroupement une seule fois, tout en effectuant une itération sur la liste résultante dans foreach, ce qui devrait être moins coûteux que d'effectuer une deuxième fois l'opération de regroupement. (Que vous pouvez mesurer une économie dépendra entièrement de la taille et/ou de la source des données dans la liste initiale. En cas de doute, le profil il.)


* Il peut y avoir un petit peine mesurée pour la couche d'indirection, vous devrez le profiler si vous pensez que c'est un vrai goulot d'étranglement. Mais pensez à la méthode Count() comme

if (sequence is IList<T>) 
{ 
    return ((IList<T>)sequence).Count 
} 
else 
{ 
    /* perform enumeration */; 
} 
+0

Merci pour la réponse complète. La taille des données dans la liste originale ne sera pas très grande mais cette opération sera beaucoup effectuée sur le thread de l'interface utilisateur. –

Questions connexes