C# 为什么LINQ方法Any不检查计数?

C# 为什么LINQ方法Any不检查计数?,c#,.net,linq,C#,.net,Linq,如果我们查看扩展方法Any的源代码,就会发现它总是使用枚举器: public static bool Any<TSource>(this IEnumerable<TSource> source) { if (source == null) throw Error.ArgumentNull(nameof (source)); using (IEnumerator<TSource> enumerator = source.GetEn

如果我们查看扩展方法
Any
的源代码,就会发现它总是使用枚举器:

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
        throw Error.ArgumentNull(nameof (source));
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
            return true;
    }
    return false;
}
我说,它可以看起来像这样:

private static bool Any<TSource>(IEnumerable<TSource> source)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    IList<TSource> sourceList = source as IList<TSource>;

    if (sourceList != null)
    {
        return sourceList.Count != 0;
    }

    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
            return true;
    }
    return false;
}
对于
IList
来说,它大约好两倍,对于
IEnumerable
来说,它大约差20%


那么,问题是:在
SingleOrDefault
方法中使用优化,而在
Any
one中不使用优化的原因是什么?

问题背后的假设可能是:

数数很快,为什么不用呢

为什么
Any
不使用它,一个看似合理的答案是
Count
并不总是快。他们选择的实现的优点是它将具有相对稳定和低成本(即大致
O(1)
)。但是,在所有情况下(如您所确定的),它可能不如
Count

无法保证实现
IList
ICollection
的类将具有fast
Count
属性,例如,
Count>0
的速度通常比现有的
任何
实现慢


此外,使用
IList
的代码可能应该使用
ICollection
,因为您的代码不需要
IList
提供访问的额外功能。

。LINQ是为IEnumerable而不是IList构建的。然而,我认为在.NET框架中支持最常见的集合类是微不足道的(但无论如何,
SingleOrDefault
使用
Count
。为什么,如果它可以很慢的话?只是不明白在哪里可以使用“slow”方法,其中notYep,这意味着
SingleOrDefault
对于任何实现
IList
且具有慢速
计数
属性@Backs的类型来说都会很慢。LINQ的本质是它是一个稍微泄漏的抽象-对于他们正在进行权衡的每个方法。有时,他们会得到正确的权衡“错误”(例如)。由于接口(
IList
ICollection
或其他任何东西),很难构建这种东西告诉您某件事是否可能,但不告诉您它的性能成本。现有的
Any
的好处是它有一个相对稳定的成本。@mjwills我写了一篇关于这个问题的小文章:.net核心存储库中有一个关于这个问题的问题:。您可以在那里阅读关于它的优缺点的第一手信息(特别是Jon Hannas的评论)。您会发现一些令人惊讶的事情,例如,对于数组,当前版本实际上比检查
ICollection.Count
(而对于列表,它稍微慢一点)。
private static bool Any<TSource>(IEnumerable<TSource> source)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    IList<TSource> sourceList = source as IList<TSource>;

    if (sourceList != null)
    {
        return sourceList.Count != 0;
    }

    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
            return true;
    }
    return false;
}
namespace AnyTests
{

    class Program
    {
        static void Main(string[] args)
        {
            BenchmarkRunner.Run<Test>();
        }
    }

    public class Test
    {
        private readonly List<int> list1 = new List<int>(new[] { 1, 2, 3, 4, 5 });

        private readonly IEnumerable<int> list2 = GetCollection();

        private static IEnumerable<int> GetCollection()
        {
            yield return 1;
        }

        [Benchmark]
        public void TestLinqAnyList()
        {
            Enumerable.Any(list1);
        }

        [Benchmark]
        public void TestNewAnyList()
        {
            NewAny(list1);
        }

        [Benchmark]
        public void TestLinqAnyEnumerable()
        {
            Enumerable.Any(list2);
        }

        [Benchmark]
        public void TestNewAnyEnumerable()
        {
            NewAny(list2);
        }


        private static bool NewAny<TSource>(IEnumerable<TSource> source)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            IList<TSource> sourceList = source as IList<TSource>;

            if (sourceList != null)
            {
                return sourceList.Count != 0;
            }

            using (IEnumerator<TSource> enumerator = source.GetEnumerator())
            {
                if (enumerator.MoveNext())
                    return true;
            }
            return false;
        }
    }
}
// * Summary *

BenchmarkDotNet=v0.10.13, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical cores and 4 physical cores
Frequency=3515624 Hz, Resolution=284.4445 ns, Timer=TSC
  [Host]     : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2600.0
  DefaultJob : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.2600.0


                Method |     Mean |     Error |    StdDev |
---------------------- |---------:|----------:|----------:|
       TestLinqAnyList | 26.80 ns | 0.1382 ns | 0.1154 ns |
        TestNewAnyList | 12.75 ns | 0.0480 ns | 0.0426 ns |
 TestLinqAnyEnumerable | 18.03 ns | 0.0947 ns | 0.0886 ns |
  TestNewAnyEnumerable | 23.51 ns | 0.0913 ns | 0.0762 ns |