LINQ Best way to use Count()

  • Thread starter Thread starter mick
  • Start date Start date
M

mick

I'm pretty new to LINQ so...

I have this line of code

var count = skills.Where(i => i.SkillType1 == SkillType.Major).Count();

which Resharper prompts me to change to

var count = skills.Count(i => i.SkillType1 == SkillType.Major);

Why? Anyone enlighten me on what the benefits of changing are?

TIA,
mick
 
I'm pretty new to LINQ so...

I have this line of code
var count = skills.Where(i => i.SkillType1 == SkillType.Major).Count();
which Resharper prompts me to change to

var count = skills.Count(i => i.SkillType1 == SkillType.Major);

Why? Anyone enlighten me on what the benefits of changing are?

The logic must be that:

skills.Count(i => i.SkillType1 == SkillType.Major)

may iterate over skills and count occurrences where condition is true while:

skills.Where(i => i.SkillType1 == SkillType.Major).Count()

may iterate over skills, create a temporary data structure with those
occurrences where condition is true and then iterate over that and
count.

Note the "may". They could also end up executing the exact same logic.
In fact I find that likely.

But the bottom line is resharper suggest a change that:
- will make the code simpler and therefore easier to read
- may make the code execute faster

Difficult to argue against that combo!

Arne
 
The logic must be that:

skills.Count(i => i.SkillType1 == SkillType.Major)

may iterate over skills and count occurrences where condition is true
while:

skills.Where(i => i.SkillType1 == SkillType.Major).Count()

may iterate over skills, create a temporary data structure with those
occurrences where condition is true and then iterate over that and
count.

Note the "may". They could also end up executing the exact same logic.
In fact I find that likely.

But the bottom line is resharper suggest a change that:
- will make the code simpler and therefore easier to read
- may make the code execute faster

Difficult to argue against that combo!

Here is an example where it does not matter
performance wise.

LINQ to SQL.

using System;
using System.Linq;
using System.Data.Linq;
using System.Data.Linq.SqlClient;
using System.Data.Linq.Mapping;
using System.Data.SqlClient;

public class LINQCount
{
[Table(Name="T1")]
public class T1
{
[Column(IsPrimaryKey=true)]
public int F1;
[Column]
public string F2;
}
public static void Main(string[] args)
{
SqlConnection con = new
SqlConnection(@"Server=ARNEPC4;Integrated Security=SSPI;Database=Test");
DataContext db = new DataContext(con);
db.Log = Console.Out;
Table<T1> t1 = db.GetTable<T1>();
int n1 = t1.Where(r => r.F1==2).Count();
Console.WriteLine(n1);
int n2 = t1.Count(r => r.F1==2);
Console.WriteLine(n1);
con.Close();
Console.ReadKey();
}
}

Output:

SELECT COUNT(*) AS [value]
FROM [T1] AS [t0]
WHERE [t0].[F1] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build:
4.0.30319.179
29

1
SELECT COUNT(*) AS [value]
FROM [T1] AS [t0]
WHERE [t0].[F1] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build:
4.0.30319.179
29

1

Exact same SQL.

Arne
 
On 02.03.13 01.01, Arne Vajhøj wrote:
[...]
Exact same SQL.

I wouldn't bet that any other LINQ provider will behave the same.
Especially in case of IEnumerable<> I am in doubt.


Marcel
 
On 02.03.13 01.01, Arne Vajhøj wrote:
[...]
Exact same SQL.

I wouldn't bet that any other LINQ provider will behave the same.

Absolutely not.

http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx

says:

<quote>
Note: Deferred execution applies to all varieties of LINQ, including
LINQ to SQL, LINQ to Objects and LINQ to XML. However, of the three, it
is only LINQ to SQL that returns an expression tree by default. Or more
specifically, it returns an instance of the IQueryable interface that
references an expression tree. A query that returns IEnumerable still
supports deferred execution, but at least some larger portion of the
result is likely to have been generated than is the case with LINQ to
SQL. In other words, all types of LINQ support deferred execution, but
LINQ to SQL supports it more fully than LINQ to Objects or LINQ to XML.
Especially in case of IEnumerable<> I am in doubt.

It seem to do in the in my .NET version.

Tested with:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace E
{
public class Wrapper<T> : IEnumerable<T>
{
private List<T> real;
public Wrapper(List<T> real)
{
this.real = real;
}
IEnumerator IEnumerable.GetEnumerator()
{
Console.WriteLine("IEnumerable.GetEnumerator");
return real.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
Console.WriteLine("IEnumerable<T>.GetEnumerator");
return real.GetEnumerator();
}
}
public static class Fake
{
public static IEnumerable<TSource> FakeWhere<TSource>(this
IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
Console.WriteLine("IEnumerable<T>.Where");
return source.Where(predicate);
}
public static int FakeCount<TSource>(this IEnumerable<TSource>
source)
{
Console.WriteLine("IEnumerable<T>.Count");
return source.Count();
}
}
public class Program
{
public static void Main(string[] args)
{
List<int> lst = new List<int> { 1, 2, 3, 4, 5 };
Console.WriteLine(lst.Where(v => v > 2 && v < 5).Count());
Wrapper<int> wrp = new Wrapper<int>(lst);
Console.WriteLine(wrp.FakeWhere(v => v > 2 && v <
5).FakeCount());
Console.ReadKey();
}
}
}

Arne
 
Back
Top