Linq does a lot of clever things such as returning the result of the Count-property using the Count() method on a IList.
Is there a good source that gives an overview of this optimizations?
It would be very interesting because as before I knew the above, I never used Count() and thus often returned a List<T> than only an IEnumerable<T> because i knew that the caller will need need often the instance-count of the list.
But having in mind that Count() does not really count the instances contained in the IEnumerable<T> but returns the result of the Count-property from the returned List and therefore not loosing performance occasioned me to change a lot of my returning types from a List to IEnumerable<T>.
Try the .NET Reflector. It's a great tool for browsing class libraries, it has a powerful decompiler which let's you view the source code pretty much as it was written.
e.g. The Count() extension method is implemented like this
if (source == null)
{
throw Error.ArgumentNull("source");
}
ICollection<TSource> is2 = source as ICollection<TSource>;
if (is2 != null)
{
return is2.Count;
}
ICollection is3 = source as ICollection;
if (is3 != null)
{
return is3.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
At the off chance that the source does not implement the collection interface you'll have to count to get the actual acount. Browsing the code this way is a great way to learn.
Current optimisations that I'm aware of:
Count uses the Count property if the sequence implements ICollection<T> and a predicate isn't used. (In .NET 4 Count is also optimised for the non-generic ICollection.)
ElementAt/ElementAtOrDefault access by index if the sequence implements IList<T>.
Last/LastOrDefault access by index if the sequence implements IList<T> and a predicate isn't used.
ToArray/ToList use the Count property to allocate memory more efficiently if the sequence implements ICollection<T>. (But neither of them optimise for ICollection.)
Optimisations that could be there but aren't:
Last/LastOrDefault don't optimise in the case where a predicate is used. There's no reason why they couldn't optimise for IList<T>, iterating backwards through the list and accessing each element by index.
SequenceEqual could optimise for ICollection<T> and ICollection, using the Count property to determine if the lists are the same length and breaking out early if they're not.
Skip could optimise for IList<T>, accessing the elements by index and starting directly at index n rather than iterating and discarding the first n elements.
ToArray/ToList could also optimise for ICollection, using the Count property to allocate memory more efficiently.
ToDictionary could optimise for ICollection<T> and ICollection, using the Count property to allocate memory more efficently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With