IEnumerable VS. IList with LINQ

If you’ve stumbled across this post, chances are, you’ve become pretty familiar with IEnumerable and how often it can be / is passed as a parameter. What you may not know is that using this high level interface, while powerful, can have severe performance implications when used with LINQ.

Why do we use IEnumerable?

We use IEnumerable because essentially it is one of highest (most generic) interfaces for dealing with lists. It’s simple, many list types can be created from it, and having it in a method declaration pretty much states that you’re passing in a list that is to be enumerated.

What can go wrong using IEnumerable?

Yo dude, you said severe performance implications get to the point! Yea I know, cool your jets; I meant it. While IEnumerable is great, there is a small detail that can mean big problems for your code: IEnumerable’s GetEnumerator enumerates your list every time you call an action on that list.

Here’s an Example

I’ve gone ahead and posted an example on dotnetfiddle so that you can see and run the code yourself. Or take a peek at the code below:

Let’s have a super simple DoWork function with an IEnumerable parameter:

Now, let’s use that function with both an IEnumerable and IList parameter:

The output you would expect would be as follows:

IEnumerable Hit Count : 5
IList Hit Count : 5

Here is the actual output:

IEnumerable Hit Count : 10
IList Hit Count : 5

What wizardry is this!? Clearly you are a magician well beyond the capabilities of modern day science and logic!

Not quite, and here’s why:

Here’s what’s going on

The fact that we’re using IEnumerable and IList actually has little to do with the problem we’re seeing. The actual problem stems from LINQ’s implementation behind each interface.

LINQ’s implementation of IEnumerable is lazy and will only evaluate the query once you use the output of that query. However, while the implementation is lazy, it also runs your query every time you use the output of the query.

LINQ’s implementation of IList enumerates through the entire list once (when you call ToList()) and then stores that output and will not enumerate the list again.

In the example that I have listed, The reason we are seeing the IEnumerable example increment the count 5 more times is because we are enumerating through the list:

  • Once when we call .Count() (.Count iterates through the results hence why the incrementin function is called 5 times) (also, as a side note, never use .Count() to check if a list has any objects in it…I merely did it to further illustrate my point).
  • Once more when we access the filteredList in the foreach loop

Here is when LINQ enumerates through the list when we call the IList example:

  • Once when we call .ToList(). The results are then saved and we do not re-run the query when for either the .Count or foreach loop.

Sooo IEnumerable is evil and I shouldn’t use it right?
There is a time and place to use IEnumerable. Taking advantage of it’s laziness can work to your benefit and when you know for sure that your list will not be enumerated more than once, it’s a pretty solid option.

On the flip side, not knowing the implementation of IEnumerable could prove dangerous to anyone using it. Let’s say for example that you used a Where and Select where the Select statement actually takes a considerably long time or uses a lot of resources. Depending on your usage, you could very easily double (or worse) the time it takes for your operations to complete since the IEnumerable implementation is re-running that LINQ query.

What should I do when my method takes in an IEnumerable parameter?

If you know for a fact that you are only going to iterate through the list once, then using the IEnumerable as-is is perfectly fine. When you should be concerned is when you are going to be accessing the list multiple times or passing it into other functions where you are not sure what actions will be made. creating an IList or array using .ToList() on your IEnumerable will guarantee that your list will only be enumerated at most once.


Leave a Reply

Your email address will not be published. Required fields are marked *