IEnumerable doesn't have a Count method
C#asp.netEnumerableC# Problem Overview
I have the following method:
public bool IsValid
{
get { return (GetRuleViolations().Count() == 0); }
}
public IEnumerable<RuleViolation> GetRuleViolations(){
//code here
}
Why is it that when I do .Count()
above it is underlined in red?
I got the following error:
> Error 1 'System.Collections.Generic.IEnumerable
C# Solutions
Solution 1 - C#
You add:
using System.Linq;
at the top of your source and make sure you've got a reference to the System.Core assembly.
Count()
is an extension method provided by the System.Linq.Enumerable
static class for LINQ to Objects, and System.Linq.Queryable
for LINQ to SQL and other out-of-process providers.
EDIT: In fact, using Count()
here is relatively inefficient (at least in LINQ to Objects). All you want to know is whether there are any elements or not, right? In that case, Any()
is a better fit:
public bool IsValid
{
get { return !GetRuleViolations().Any(); }
}
Solution 2 - C#
Any()
or Count()
methods in Linq work only for generic types.
IEnumerable<T>
If you have a simple IEnumerable
without a type, try to use
IEnumerable<object>
instead.
Solution 3 - C#
IEnumeration
does not have a method called Count()
. It's just a kind of "sequence of elements". Use for example List
if you explicitly need the number of elements.
If you use Linq keep in mind, that the extension method Count()
may actually re-count the number of elements each time you call it.
Solution 4 - C#
A short & sweet general word of caution on the pitfalls of .Count() to help the weary traveler that stumbles upon this post in the future!
Short story:
The following works -no doubt- but there might small performance penalty if the enumerables is not backed by an underlying array or list which has the 'count' in handy/precomputed:
public bool IsValid
{
get { return SomeMethodReturningEnumerable().Count() <= threshold; } <--- small performance issue here
}
public IEnumerable<SomeObject> SomeMethodReturningEnumerable(){
yield return foo;
yield return bar; etc
}
The call to the .Count() method will probably go through each and every item in the enumerable and then compare the overall count against threshold. We being smarter can do a bit better:
public bool IsValid
{
get { return !SomeMethodReturningEnumerable().HasMoreThan(threshold); } <--- neato!
}
public static bool HasLessThan<T>(this IEnumerable<T> sequence, int count) => !sequence.HasMoreThan(count - 1);
public static bool HasLessOrEqualTo<T>(this IEnumerable<T> sequence, int count) => !sequence.HasMoreThan(count);
public static bool HasMoreOrEqualTo<T>(this IEnumerable<T> sequence, int count) => sequence.HasMoreThan(count - 1);
public static bool HasMoreThan<T>(this IEnumerable<T> sequence, int count) => sequence.EnumerationCounterImpl(count, equals_vs_greaterThan: false);
public static bool HasExactly<T>(this IEnumerable<T> sequence, int count) => sequence.EnumerationCounterImpl(count, equals_vs_greaterThan: true);
public static bool EnumerationCounterImpl<T>(this IEnumerable<T> sequence, int count, bool equals_vs_greaterThan = true) //0
{
if (equals_vs_greaterThan && count < 0)
throw new ArgumentException($"{nameof(count)} is less than zero!");
if (!equals_vs_greaterThan && count < 0)
return true;
var staticCount = (sequence as ICollection)?.Count
?? (sequence as ICollection<T>)?.Count
?? (sequence as IReadOnlyCollection<T>)?.Count;
if (staticCount != null)
return staticCount > count;
using (var enumerator = sequence.GetEnumerator()) //1 optimization
{
for (int i = 0; i < count + 1; i++)
{
if (enumerator.MoveNext())
continue;
return false;
}
return !equals_vs_greaterThan // ==
|| enumerator.MoveNext(); // >
}
//0 https://blog.slaks.net/2015-01-12/linq-count-considered-occasionally-harmful/
//1 using the enumerator directly is slightly faster than using LINQ methods it avoids allocating an extra iterator
// state machine compared to using skip()
}
There! Problem solved again but this time around we are performance-conscious!
Solution 5 - C#
How about:
public bool IsValid
{
get { return (GetRuleViolations().Cast<RuleViolation>().Count() == 0); }
}