.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,那么我们不能遍历所有项,因为我们可以检索ICollection的Count字段,如果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();
}
}