.net 哪种方法性能更好:.Any()与.Count()>;0?

.net 哪种方法性能更好:.Any()与.Count()>;0?,.net,linq,performance,.net-3.5,extension-methods,.net,Linq,Performance,.net 3.5,Extension Methods,在System.Linq名称空间中,我们现在可以扩展我们的名称空间,以拥有和扩展方法 最近有人告诉我,如果我想检查集合中是否包含1个或多个项,我应该使用.Any()扩展方法而不是.Count()>0扩展方法,因为.Count()扩展方法必须遍历所有项 其次,有些集合有一个属性(不是扩展方法),即Count或Length。使用它们是否比使用.Any()或.Count()更好 是/否?如果您从具有.Length或.Count的内容开始(例如ICollection,IList,List等),那么这将是

System.Linq
名称空间中,我们现在可以扩展我们的名称空间,以拥有和扩展方法

最近有人告诉我,如果我想检查集合中是否包含1个或多个项,我应该使用
.Any()
扩展方法而不是
.Count()>0
扩展方法,因为
.Count()
扩展方法必须遍历所有项

其次,有些集合有一个属性(不是扩展方法),即
Count
Length
。使用它们是否比使用
.Any()
.Count()
更好


是/否?

如果您从具有
.Length
.Count
的内容开始(例如
ICollection
IList
List
等),那么这将是最快的选择,因为它不需要通过
GetEnumerator()
/
移动下一步()
/
处理()
Any()
检查非空的
IEnumerable
序列所需的序列

对于仅
IEnumerable
,则
Any()
通常会更快,因为它只需查看一次迭代。但是,请注意,
Count()
的LINQ to Objects实现会检查
ICollection
(使用
.Count
作为优化)-因此,如果您的底层数据源直接是列表/集合,则不会有太大的差异。不要问我为什么它不使用非泛型的
ICollection

当然,如果您使用LINQ对其进行过滤等(
Where
etc),您将拥有一个基于迭代器块的序列,因此这种
ICollection
优化是无用的


通常使用
IEnumerable
:坚持使用
Any()
-p

注意:我在实体框架4实际运行时写下了这个答案。这个答案的重点不是要进行琐碎的
.Any()
.Count()
性能测试。这一点是为了表明EF远非完美。更新的版本更好。。。但是,如果您有一部分代码比较慢,并且它使用EF,请使用direct TSQL进行测试并比较性能,而不是依赖于假设(即
.Any()
总是比
.Count()>0
快)


虽然我同意大多数投赞成票的答案和评论,特别是在
Any
Count()>0
更能表明开发人员的意图这一点上,但在SQL Server(EntityFramework 4)上,我遇到过计数按数量级加快的情况

下面是使用
Any
查询W超时异常(在约200.000条记录上):

我需要找到一种方法来查看两个LINQ都产生了什么样的确切SQL—但很明显,在某些情况下,
Count
Any
之间存在巨大的性能差异,不幸的是,您似乎无法在所有情况下都坚持使用
Any

编辑:这里是生成的SQL。如你所见的美丽;)

任何

exec sp_executesql N'SELECT TOP (1) [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created] FROM ( SELECT [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created], row_number() OVER (ORDER BY [Project2].[ContactId] ASC) AS [row_number] FROM ( SELECT [Extent1].[ContactId] AS [ContactId], [Extent1].[CompanyId] AS [CompanyId], [Extent1].[ContactName] AS [ContactName], [Extent1].[FullName] AS [FullName], [Extent1].[ContactStatusId] AS [ContactStatusId], [Extent1].[Created] AS [Created] FROM [dbo].[Contact] AS [Extent1] WHERE ([Extent1].[CompanyId] = @p__linq__0) AND ([Extent1].[ContactStatusId] <= 3) AND ( NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[NewsletterLog] AS [Extent2] WHERE ([Extent1].[ContactId] = [Extent2].[ContactId]) AND (6 = [Extent2].[NewsletterLogTypeId]) )) ) AS [Project2] ) AS [Project2] WHERE [Project2].[row_number] > 99 ORDER BY [Project2].[ContactId] ASC',N'@p__linq__0 int',@p__linq__0=4 似乎纯Where with EXISTS比计算Count然后在Where with Count==0时执行更糟糕


如果你们发现我的发现有错误,请告诉我。不管对vs计数的讨论如何,都可以从中得到的是,当重写为存储过程时,任何更复杂的LINQ都要好得多;)

编辑:它在EF版本6.1.1中已修复。这个答案并不真实

对于SQL Server和EF4-6,Count()的执行速度大约是任何()的两倍

当您运行Table.Any()时,它将生成如下内容(警告:不要试图理解它而伤害大脑)

这需要对符合您条件的行进行两次扫描

我不喜欢写
Count()>0
,因为它隐藏了我的意图。我更喜欢对此使用自定义谓词:

public static class QueryExtensions
{
    public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
    {
        return source.Count(predicate) > 0;
    }
}
公共静态类查询扩展
{
存在公共静态bool(此IQueryable源、表达式谓词)
{
返回source.Count(谓词)>0;
}
}

这取决于数据集有多大,以及您的性能要求是什么

如果没什么大不了的,那就用最可读的形式,
对我来说,这是任何一个问题,因为它比一个等式更简短易读。

因为这是一个相当流行的话题,答案也不尽相同,所以我不得不重新审视这个问题

测试环境: EF 6.1.3,SQL Server,300k记录

表格模型

class TestTable
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public string Surname { get; set; }
}
测试代码:

class Program
{
    static void Main()
    {
        using (var context = new TestContext())
        {
            context.Database.Log = Console.WriteLine;

            context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);

            Console.ReadLine();
        }
    }
}
结果:

class Program
{
    static void Main()
    {
        using (var context = new TestContext())
        {
            context.Database.Log = Console.WriteLine;

            context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);

            Console.ReadLine();
        }
    }
}
任何()~3ms

Count()第一次查询约230ms,第二次查询约400ms

备注:

class Program
{
    static void Main()
    {
        using (var context = new TestContext())
        {
            context.Database.Log = Console.WriteLine;

            context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
            context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);

            Console.ReadLine();
        }
    }
}

在我的例子中,EF没有像@Ben在他的帖子中提到的那样生成SQL。

你可以做一个简单的测试来解决这个问题:

var query = //make any query here
var timeCount = new Stopwatch();
timeCount.Start();
if (query.Count > 0)
{
}
timeCount.Stop();
var testCount = timeCount.Elapsed;

var timeAny = new Stopwatch();
timeAny.Start();
if (query.Any())
{
}
timeAny.Stop();
var testAny = timeAny.Elapsed;
检查testCount和testAny的值。

关于Count()方法,如果IEnumarable是一个ICollection,那么我们不能遍历所有项,因为我们可以检索ICollectionCount字段,如果IEnumerable不是ICollection,我们必须使用对所有项目进行迭代,而使用MoveNext对所有项目进行迭代,请查看.NET Framework代码:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) 
        throw Error.ArgumentNull("source");

    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) 
        return collectionoft.Count;

    ICollection collection = source as ICollection;
    if (collection != null) 
        return collection.Count;

    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        checked
        {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}
公共静态整数计数(此IEnumerable源)
{
if(source==null)
抛出错误。ArgumentNull(“源”);
ICollection collectionoft=源作为ICollection;
if(collectionoft!=null)
返回collectionoft.Count;
ICollection collection=源作为ICollection;
if(集合!=null)
返回集合。计数;
整数计数=0;
使用(IEnumerator e=source.GetEnumerator())
{
选中的
{
而(e.MoveNext())count++;
}
}
返回计数;
}
参考资料:

如果您是
var query = //make any query here
var timeCount = new Stopwatch();
timeCount.Start();
if (query.Count > 0)
{
}
timeCount.Stop();
var testCount = timeCount.Elapsed;

var timeAny = new Stopwatch();
timeAny.Start();
if (query.Any())
{
}
timeAny.Stop();
var testAny = timeAny.Elapsed;
public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) 
        throw Error.ArgumentNull("source");

    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) 
        return collectionoft.Count;

    ICollection collection = source as ICollection;
    if (collection != null) 
        return collection.Count;

    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        checked
        {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}
    private int _size;

    public int Count {
        get {
            Contract.Ensures(Contract.Result<int>() >= 0);
            return _size; 
        }
    }
public static bool Any<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    using (IEnumerator<TSource> e = source.GetEnumerator()) {
        if (e.MoveNext()) return true;
    }
    return false;
}
public static int Count<TSource>(this IEnumerable<TSource> source)
{
    ICollection<TSource> collection = source as ICollection<TSource>;
    if (collection != null)
    { 
        return collection.Count;
    }
    int num = 0;
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            num = checked(num + 1);
        }
        return num;
    }
}
    public static bool Any<TSource>(this IEnumerable<TSource> source)
    {
        //..snip..
        
        if (source is ICollection<TSource> collectionoft)
        {
            return collectionoft.Count != 0;
        }
        
        //..snip..

        using (IEnumerator<TSource> e = source.GetEnumerator())
        {
            return e.MoveNext();
        }
    }