An IEnumerable object does not represent a sequence of objects itself, it represents the algorithm needed to give you upon request the first element of the sequence as "current element" , and to give you the next element after the current element.
When linq was invented, it was decided that linq uses the concept of deferred execution, quite often called lazy evaluation. In the MSDN description of Enumerable functions that use deferred execution you will find the following phrase:
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach.
If you create the IEnumerable, and change the objects on which the IEnumerable object acts, this change might influence the result. It is comparable to a function that returns a different value if the parameters on which the function acts are changed:
int x = 4;
int y = 5;
int MyFunction()
{
return x + y;
}
int a = MyFunction();
y = 7;
int b = MyFunction();
Now b does not equal a. Similar to your IEnumerable:
List<...> myList = CreateMySequence()
var IEnumerable<...> myOrder = myList.OrderBy(...);
myOrder does not contain the result, but is like a function that can calculate the result for it. If you change one of the parameters that myOrder uses, the result might change:
myList.Add(someElement);
var myResult = myOrder.ToList();
myResult has changed, because you changed the function.
The reason that deferred execution was invented, is because quite often you don't need to enumerate over all elements of the sequence. In the following cases it would be a wast of processing time if you'd create the complete sequence:
- I want only the first element,
- I want to skip 3 elements and then take two elements,
- I want the first element with a value of x
- I want to know if the sequence contains any element at all
Of course there are functions that need to create the complete sequence as soon as you ask for the first element:
- If you want the first in a sorted sequence, all elements have to be sorted in order to find the first one.
- If you want the first element of a group of elements where all elements in the group have the same value of a certain property X (Enumerable.GroupBy)
As a rule of thumb it is wise to keep all sequences as IEnumerable as long as possible until you either need the results, or until the sources that are used to create the sequence are changed.
This latter is important when fetching data from a database, from a file, from the internet: you'll have to create the sequence before your connection is closed.
The following wont't work
using (var myDbContext = new MyDbContext)
{
return MyDbContext.Customers.Where(customer => customer.Age > 18);
}
The database query is not executed before you Disposed myDbContext when leaving the using statement. Therefore you'll get an exception as soon as you ask for any element in the sequence.