C# Count() 扩展方法性能

发布于 2024-08-09 06:43:15 字数 3052 浏览 9 评论 0原文

如果在具有 Count 属性(例如 List)的 IEnumerable 上调用 LINQ Count() 扩展方法;),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 的自定义集合如何公开其自己的 Count 属性以使得 CLR Count() 扩展方法可以利用它?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

铁憨憨 2024-08-16 06:43:15

它不会按名称查找 Count 属性,但会检查它是否实现 ICollection,然后使用该类型的 Count 属性。来自文档

如果源类型实现
ICollection,即
实现用于获取
元素计数。否则,这
方法确定计数。

(显然这仅适用于不采用谓词的重载。)

因此,如果您想有效地获取计数,请确保实现ICollection

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>.

怎言笑 2024-08-16 06:43:15

是的,Enumerable.Count 方法确实会查找 ICollection 并在找到时使用它的 Count 属性。您可以通过查看反射器中的 Enumerable.Count 来验证这一点。

仅当您使用不带附加参数的 Count 扩展方法时,这才是正确的。如果您使用带有谓词的版本,它将遍历可枚举元素。

Yes, the Enumerable.Count method will indeed look for ICollection<T> and use it's Count property if found. You can verify this by looking at Enumerable.Count in reflector.

This is only true though if you use the Count extension method which takes no additional parameters. If you use the version which takes a predicate it will walk the enumerable elements.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文