IEnumerable<T>
contains a small subset of what is inside List<T>
, which contains the same stuff as IEnumerable<T>
but more! You only use IEnumerable<T>
if you want a smaller set of features. Use List<T>
if you plan to use a larger, richer set of features.
The Pizza Explanation
Here is a much more comprehensive explanation of why you would use an Interface like IEnumerable<T>
versus List<T>
, or vise versa, when instantiating objects in C languages like Microsoft C#.
Think of Interfaces like IEnumerable<T>
and IList<T>
as the individual ingredients in a pizza (pepperoni, mushrooms, black olives...) and concrete classes like List<T>
as the pizza. List<T>
is in fact a Supreme Pizza that always contains all the Interface ingredients combined (ICollection, IEnumerable, IList, etc).
What you get as far as a pizza and its toppings is determined by how you "type" your list when you create its object reference in memory. You have to declare the type of pizza you are cooking as follows:
// Pepperoni Pizza: This gives you a single Interface's members,
// or a pizza with one topping because List<T> is limited to
// acting like an IEnumerable<T> type.
IEnumerable<string> pepperoniPizza = new List<string>();
// Supreme Pizza: This gives you access to ALL 8 Interface
// members combined or a pizza with ALL the ingredients
// because List type uses all Interfaces!!
IList<string> supremePizza = new List<string>();
Note you cannot instantiate an Interface as itself (or eat raw pepperoni). When you instantiate List<T>
as one Interface type like IEnumerable<T>
you only have access to its Implementations and get the pepperoni pizza with one topping. You can only access IEnumerable<T>
members and cannot see all the other Interface members in List<T>
.
When List<T>
is instantiated as IList<T>
it implements all 8 Interfaces, so it has access to all the members of all the Interfaces it has implemented (or a Supreme Pizza toppings)!
Here is the List<T>
class, showing you WHY that is. Notice the List<T>
in the .NET Library has implemented all the other Interfaces including IList!! But IEnumerable<T>
implements just a small subsection of those List Interface members.
public class List<T> :
ICollection<T>,
IEnumerable<T>,
IEnumerable,
IList<T>,
IReadOnlyCollection<T>,
IReadOnlyList<T>,
ICollection,
IList
{
// List<T> types implement all these goodies and more!
public List();
public List(IEnumerable<T> collection);
public List(int capacity);
public T this[int index] { get; set; }
public int Count { get; }
public int Capacity { get; set; }
public void Add(T item);
public void AddRange(IEnumerable<T> collection);
public ReadOnlyCollection<T> AsReadOnly();
public bool Exists(Predicate<T> match);
public T Find(Predicate<T> match);
public void ForEach(Action<T> action);
public void RemoveAt(int index);
public void Sort(Comparison<T> comparison);
// ......and much more....
}
So why NOT instantiate List<T>
as List<T>
ALL THE TIME?
Instantiating a List<T>
as List<T>
gives you access to all Interface members! But you might not need everything. Choosing one Interface type allows your application to store a smaller object with less members and keeps your application tight. Who needs Supreme Pizza every time?
But there is a second reason for using Interface types: Flexibility. Because other types in .NET, including your own custom ones, might use the same "popular" Interface type, it means you can later substitute your List<T>
type with any other type that implements, say IEnumerable<T>
. If your variable is an Interface type, you can now switch out the object created with something other than List<T>
. Dependency Injection is a good example of this type of flexibility using Interfaces rather than concrete types, and why you might want to create objects using Interfaces.