Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
630 views
in Technique[技术] by (71.8m points)

linq - C# Count() Extension Method Performance

If the LINQ Count() extension method is invoked on an IEnumerable<T> that has a Count property (e.g. List<T>), does the Count() method look for that property and return it (rather than counting the items by enumerating them)? The following test code seems to indicate that it does:

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

On a related note: how can a custom collection which implements IEnumerable<T> expose its own Count property in such a way that the CLR Count() extension method can take advantage of it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It doesn't look for a Count property by name, but it does check whether it implements ICollection<T>, and then uses that type's Count property. From the documentation:

If the type of source implements ICollection<T>, that implementation is used to obtain the count of elements. Otherwise, this method determines the count.

(Obviously this only applies to the overload which doesn't take a predicate.)

So, if you want to obtain the count efficiently, make sure you implement ICollection<T>.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...