LINQ Best way to use Count()

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
 
A

Arne Vajhøj

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
 
A

Arne Vajhøj

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
 
M

Marcel Müller

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
 
A

Arne Vajhøj

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top