2009-10-30 5 views
2

Если метод расширения LINQ Count() вызывается на IEnumerable<T>, который имеет Count свойство (например, List<T>), делает Count() метод взгляд на это имущество и вернуть его (а не считая пунктов, перечисляя их)? Следующий тестовый код, кажется, указывает, что он делает:C# Count() метод расширения производительности

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(); } 
    } 
} 

На соответствующую записку: как можно настраиваемая коллекция, которая реализует IEnumerable<T> выставить свою собственную Count собственность таким образом, что метод расширения CLR Count() может принимать это преимущество?

ответ

11

Он не ищет свойства Count по имени, но он проверяет, реализует ли он ICollection<T>, а затем использует свойство Count этого типа. Из documentation:

Если тип источника реализует ICollection<T>, который используется реализации для получения количества элементов. В противном случае этот метод определяет счетчик.

(Очевидно, что это относится только к перегрузке, которая не принимает предикат.)

Итак, если вы хотите, чтобы эффективно получить счет, убедитесь, что вы реализуете ICollection<T>.

5

Да, метод Enumerable.Count действительно ищет ICollection<T> и использует его свойство Count, если найдено. Вы можете проверить это, посмотрев Enumerable.Count в отражателе.

Это верно только в том случае, если вы используете метод расширения Count, который не принимает дополнительных параметров. Если вы используете версию, которая берет предикат, она будет перебирать перечислимые элементы.

Смежные вопросы