C#:空列表的Any()vs Count()

C#:空列表的Any()vs Count(),c#,.net,C#,.net,早些时候发布的一条消息让我思考。在空列表中使用时,Any()和Count()是否会执行类似的操作 如前所述,两者都应该经历相同的步骤GetEnumerator()/MoveNext()/Dispose() 我在LINQPad上使用quick程序对此进行了测试: static void Main() { var list = new List<int>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.S

早些时候发布的一条消息让我思考。在空列表中使用时,
Any()
Count()
是否会执行类似的操作

如前所述,两者都应该经历相同的步骤
GetEnumerator()/MoveNext()/Dispose()

我在LINQPad上使用quick程序对此进行了测试:

static void Main()
 {
    var list = new List<int>();

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    for (int i = 0; i < 10000; i++)
        list.Any();

    stopwatch.Stop();
    Console.WriteLine("Time elapsed for Any()   : {0}", stopwatch.Elapsed);


    stopwatch = new Stopwatch();
    stopwatch.Start();

    for (int i = 0; i < 10000; i++)
        list.Count();

    stopwatch.Stop();
    Console.WriteLine("Time elapsed for Count(): {0}", stopwatch.Elapsed);
}
static void Main()
{
var list=新列表();
秒表秒表=新秒表();
秒表。开始();
对于(int i=0;i<10000;i++)
list.Any();
秒表;
WriteLine(“Any()经过的时间:{0}”,stopwatch.appeased);
秒表=新秒表();
秒表。开始();
对于(int i=0;i<10000;i++)
list.Count();
秒表;
WriteLine(“Count()经过的时间:{0}”,stopwatch.appeased);
}
一般结果似乎表明,
Count()
在这种情况下速度更快。为什么呢

我不确定我是否得到了正确的基准,如果没有,我将不胜感激


编辑:我理解它在语义上更有意义。我在问题中发布的第一个链接显示了一种情况,即直接使用
Count()
是有意义的,因为将使用值,因此问题就出现了。

方法针对
ICollection
类型进行了优化,因此不使用模式
GetEnumerator()/MoveNext()/Dispose()

list.Count();
翻译成

((ICollection)list).Count;
Any()
必须构建一个枚举器。 因此
Count()
方法更快

这里是4个不同的
IEnumerable
实例的基准测试。
MyEmpty
看起来像
IEnumerable MyEmpty(){yield break;}

iterations : 100000000

Function                      Any()     Count()
new List<int>()               4.310     2.252
Enumerable.Empty<int>()       3.623     6.975
new int[0]                    3.960     7.036
MyEmpty<int>()                5.631     7.194

你的基准测试到底显示了什么?我认为只要调用这其中的任何一个10000次都会非常快,以至于无法进行合理的测量。一般来说,
Any
更好的原因是,它只需要在枚举中找到一个东西,而count需要找到所有的东西。在您的测试中,列表是空的,因此很明显,查找第一个列表和查找所有列表没有多大区别
请注意,Count()的LINQ to Objects实现确实检查ICollection(使用.Count作为优化)-因此,如果您的基础数据源直接是列表/集合,实际上,您已经链接到了回答此问题的问题,这与
没有太大区别。正如上面提到的,在我看来,最好选择语义上更准确的方法(除非你是在CPU限制的关键路径上,但这是非常罕见的)。虽然理论上很有趣,但我认为这个问题没有什么价值:如果你已经知道列表是空的,你不需要调用任何一个方法;如果您不知道,您应该更喜欢
Any
而不是
Count
。使用
Enumerable.Empty()
表明
Any()
Count()快。谢谢-1:你的测试不正确
Enumerable.Empty()
返回一个空数组,数组实现扩展
ICollection
IList
。您需要一种只做
产生中断
的方法。该调用实质上是在测试中嗅出相同的代码路径。
class Program
{
    public const long Iterations = (long)1e8;

    static void Main()
    {
        var results = new Dictionary<string, Tuple<TimeSpan, TimeSpan>>();
        results.Add("new List<int>()", Benchmark(new List<int>(), Iterations));
        results.Add("Enumerable.Empty<int>()", Benchmark(Enumerable.Empty<int>(), Iterations));
        results.Add("new int[0]", Benchmark(new int[0], Iterations));
        results.Add("MyEmpty<int>()", Benchmark(MyEmpty<int>(), Iterations));

        Console.WriteLine("Function".PadRight(30) + "Any()".PadRight(10) + "Count()");
        foreach (var result in results)
        {
            Console.WriteLine("{0}{1}{2}", result.Key.PadRight(30), Math.Round(result.Value.Item1.TotalSeconds, 3).ToString().PadRight(10), Math.Round(result.Value.Item2.TotalSeconds, 3));
        }
        Console.ReadLine();
    }

    public static Tuple<TimeSpan, TimeSpan> Benchmark(IEnumerable<int> source, long iterations)
    {
        var anyWatch = new Stopwatch();
        anyWatch.Start();
        for (long i = 0; i < iterations; i++) source.Any();
        anyWatch.Stop();

        var countWatch = new Stopwatch();
        countWatch.Start();
        for (long i = 0; i < iterations; i++) source.Count();
        countWatch.Stop();

        return new Tuple<TimeSpan, TimeSpan>(anyWatch.Elapsed, countWatch.Elapsed);
    }

    public static IEnumerable<T> MyEmpty<T>() { yield break; }
}