Visually Located

XAML and GIS

Please, please, please stop using IEnumerable.Count()

As the title says, please, please, please stop using the IEnumerabler Count() extension method UNLESS you must have the count. Over and over again I see this small little snippet

 1: if(myEnumerable.Count() > 0)
 2: {
 3:     // Do stuff
 4: }

This has a huge performance impact on your application. Let’s take a look at what this code is actually doing (decompiled using DotPeek 1.1)

 1: public static int Count<TSource>(this IEnumerable<TSource> source)
 2: {
 3:     if (source == null)
 4:         throw Error.ArgumentNull("source");
 5:     ICollection<TSource> collection1 = source as ICollection<TSource>;
 6:     if (collection1 != null)
 7:         return collection1.Count;
 8:     ICollection collection2 = source as ICollection;
 9:     if (collection2 != null)
 10:         return collection2.Count;
 11:     int num = 0;
 12:     using (IEnumerator<TSource> enumerator = source.GetEnumerator())
 13:     {
 14:         while (enumerator.MoveNext())
 15:             checked { ++num; }
 16:     }
 17:     return num;
 18: }

If your IEnumerable<T> collection supports ICollection<T> then all is well, you'll be accessing a property that already has a value. Many times this is not the case. A lot of APIs will only expose collections as IEnumerable<T>. This follows the Frame Design Guidelines to only expose the minimum (base) interface that you need to. In these cases it must iterate over every single item in your collection to get the count. And all that just to decide if there is at least one item in the collection?!?

Let’s take a look at the Any() extension method.

 1: public static bool Any<TSource>(this IEnumerable<TSource> source)
 2: {
 3:     if (source == null)
 4:         throw Error.ArgumentNull("source");
 5:     using (IEnumerator<TSource> enumerator = source.GetEnumerator())
 6:     {
 7:         if (enumerator.MoveNext())
 8:             return true;
 9:     }
 10:     return false;
 11: }

This method just determines if there is one item in the collection. It does this by attempting to move the iterator one spot. If it can then the collection has an item!. Now, I would probably change lines 7 and 8 to be

 7: return enumerator.MoveNext();

This would remove the need for the if check. Again, this is decompiled code so it’s possible that the actual code is doing this.

So, if you need to know if the collection contains at least one item, and you’re working with an IEnumerable<T>, use the Any() method.

If you want to squeeze every little ounce of performance out of your app continue reading! What I find odd is that the Any() extension method does not try to take advantage of the two most common collections, arrays and ICollection<T>. Both of these collections have a ready made property to aid in this effort. Array has the Length property and ICollection<T> has the Count property. By making a new extension method that takes advantage of these properties you can shrink your calls to Any() in half.

NOTE: In half is very minimal. We’re talking about ticks, not even milliseconds unless it is called MANY MANY times.

   1: public static bool FastAny<TSource>(this IEnumerable<TSource> source)
   2: {
   3:     if (source == null) throw new ArgumentNullException("source");
   4:  
   5:     var array = source as TSource[];
   6:     if (array != null) return array.Length > 0;
   7:  
   8:     var coll = source as ICollection<TSource>;
   9:     if (coll != null) return coll.Count > 0;
  10:  
  11:     using (var enumerator = source.GetEnumerator())
  12:     {
  13:         return enumerator.MoveNext();
  14:     }
  15: }
If you’d like to test this out for yourself, you can run this console application.
blog comments powered by Disqus