2009-10-30 12 views
2

Si la méthode d'extension LINQ Count() est invoquée sur un IEnumerable<T> qui a une Count propriété (par exemple List<T>), fait l'aspect de la méthode Count() pour cette propriété et le retourner (au lieu de compter les éléments en dénombrant leur)? Le code de test suivant semble indiquer qu'il fait:C# Count() Méthode d'extension Performance

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 

namespace CountSpeedTest 
{ 
    // Output: 
    // List  - CLR : 0 ms 
    // Enumerate - CLR : 10 ms 
    // List  - Mine: 12 ms 
    // Enumerate - Mine: 12 ms 
    class Program 
    { 
     private const int Runs = 10; 
     private const int Items = 1000000; 

     static void Main(string[] args) 
     { 
      var total = new long[] {0, 0, 0, 0}; 
      for (int i = 0; i < Runs; ++i) 
      { 
       var items = Enumerable.Range(0, Items).Select(o => o.ToString()).ToList(); 
       var list = new List<string>(items); 
       var enumerate = new Enumerate<string>(items); 
       total[0] += TimeCount(list, c => c.Count()); 
       total[1] += TimeCount(enumerate, c => c.Count()); 
       total[2] += TimeCount(list, c => c.SlowCount()); 
       total[3] += TimeCount(enumerate, c => c.SlowCount()); 
      } 
      Console.WriteLine(String.Format("List  - CLR : {0} ms", total[0]/Runs)); 
      Console.WriteLine(String.Format("Enumerate - CLR : {0} ms", total[1]/Runs)); 
      Console.WriteLine(String.Format("List  - Mine: {0} ms", total[2]/Runs)); 
      Console.WriteLine(String.Format("Enumerate - Mine: {0} ms", total[3]/Runs)); 
      Console.ReadKey(true); 
     } 

     private static long TimeCount<T>(IEnumerable<T> collection, Func<IEnumerable<T>, int> counter) 
     { 
      var stopwatch = Stopwatch.StartNew(); 
      var count = counter(collection); 
      stopwatch.Stop(); 
      if (count != Items) throw new Exception("Incorrect Count"); 
      return stopwatch.ElapsedMilliseconds; 
     } 
    } 

    public static class CountExtensions 
    { 
     // Performs a simple enumeration based count. 
     public static int SlowCount<T>(this IEnumerable<T> items) 
     { 
      var i = 0; 
      var enumerator = items.GetEnumerator(); 
      while (enumerator.MoveNext()) i++; 
      return i; 
     } 
    } 

    // Wraps an IEnumerable<T> to hide its Count property. 
    public class Enumerate<T> : IEnumerable<T> 
    { 
     private readonly IEnumerable<T> collection; 
     public Enumerate(IEnumerable<T> collection) { this.collection = collection; } 

     public IEnumerator<T> GetEnumerator() { return collection.GetEnumerator(); } 
     IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } 
    } 
} 

Sur une note connexe: comment une collection personnalisée qui implémente IEnumerable<T> exposer ses propres Count propriété de telle sorte que la méthode d'extension CLR Count() peut prendre avantage de cela?

Répondre

11

Il ne recherche pas une propriété Count par son nom, mais il vérifie s'il implémente ICollection<T>, puis utilise la propriété Count de ce type. De l'documentation:

Si le type de source implémente ICollection<T>, que la mise en œuvre est utilisé pour obtenir le nombre d'éléments. Sinon, cette méthode détermine le nombre.

(Il est évident que cela ne concerne que la surcharge qui ne prend pas un prédicat.)

Donc, si vous voulez obtenir le nombre efficace, assurez-vous de mettre en œuvre ICollection<T>.

5

Oui, la méthode Enumerable.Count recherche en effet ICollection<T> et utilise sa propriété Count si elle est trouvée. Vous pouvez le vérifier en regardant Enumerable.Count dans le réflecteur.

Ceci n'est vrai que si vous utilisez la méthode d'extension Count qui ne prend pas de paramètres supplémentaires. Si vous utilisez la version qui prend un prédicat, il va parcourir les éléments énumérables.