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