What's the difference between IEnumerable
and Array
?
What's the difference between IList
and List
?
These seem to have the same function.
IEnumerable
provides only minimal "iterable" functionality. You can traverse the sequence, but that's about it.
This has disadvantages; for example, it is very inefficient to count elements using IEnumerable
, or to get the nth element.
But it has advantages too; for example, an IEnumerable
could be an endless sequence, like the sequence of primes.
Array
is a fixed-size collection with random access (i.e. you can index into it).
List
is a variable-size collection (i.e. you can add and remove elements) with random access.
IList
is an interface which abstracts list functionality (count, add, remove, indexer access) away from the various concrete classes such as List
, BindingList
, ObservableCollection
, etc.
IEnumerable is an interface that allows the iteration through a collection of items (e.g. via the foreach keyword).
An array is a .NET intrinsic. It holds items of the same type, but it is of a fixed size. Once you create an array with x elements, it cannot grow or shrink.
IList defines the interface for a list, and also implements IEnumerable.
List implements the IList interface; it is a concrete type of list.
The difference between .NET Lists and arrays is that lists can have elements added to them -- they grow to be big enough to hold all of the required items. The list stores this internally in an array and, when the array is no longer big enough to hold all of the elements, a new array is created and the items copied across.
IList & arrays both implement IEnumerable. That's how interfaces work -- classes implement the contract and behave in a similar fashion and can be treated similarly as a result (you know that the class implements IEnumerable, you don't need to know the hows or the whys). I suggest you read up on interfaces and so forth.
Generation of an IEnumerable collection is lazy. Example:
public IEnumerable<int> GetTwoInts()
{
yield return 1;
yield return 2;
}
public void Something()
{
var twoInts = GetTwoInts();
}
In the method Something the call to GetTwoInts() will not actually result in the method GetTwoInts being executed since the enumeration is never iterated over.
IEnumerable and IList are interfaces. Array and List are classes. Array implements IEnumerable. List implements IList which extends IEnumerable.
Edit: as itowlson mentionned in a comment, Array also implements IList.
To complement the other answers, note that there is a performance difference between IList<T>
and List<T>
when executing a foreach statement.
That's because the iterator object returned by List<T>.GetEnumerator
is a value-type whereas the one returned by IList<T>.GetEnumerator
is a reference-type, and thus requires a memory allocation (see Enumerator of value type of list in c#).
In my opinion, IList<T>
is not a very good interface anyway. For instance calling Add
can throw (see Why array implements IList?). If you need encapsulation you'd be better off using IEnumerable<T>
or IReadOnlyList<T>
.
In addition to other answers, understanding the different between Enumerable vs List/Array when using LINQ can have huge performance impact. In short, Enumerable can be perceived as a query builder while List/Array is the result of the query.
In the context of LINQ to SQL using EntityFramework, the former is just building the SQL query without executing it against the database nor loading any data into memory while the later is the opposite. That's why we would defer calling .ToList()
until we need it in the memory to perform business logic.
In other context, LINQ expression returning IEnumerable will defer execution until .ToList()
is called. Consider example below:
void Main()
{
var w1 = "AB".AsEnumerable();
Console.WriteLine($"W1: 1");
w1 = w1.Where(W1);
Console.WriteLine($"W1: 2");
w1 = w1.Where(W2);
Console.WriteLine($"W1: 3");
w1.ToList();
Console.WriteLine($"----------");
var w2 = "CD".AsEnumerable();
Console.WriteLine($"W2: 1");
w2 = w2.Where(W1);
Console.WriteLine($"W2: 2");
w2 = w2.ToList();
Console.WriteLine($"W2: 3");
w2 = w2.Where(W2);
Console.WriteLine($"W2: 4");
w2.ToList();
Console.WriteLine($"----------");
}
bool W1(char arg)
{
Console.WriteLine($"W1:{arg}");
return true;
}
bool W2(char arg)
{
Console.WriteLine($"W2:{arg}");
return true;
}
OUTPUT:
W1: 1
W1: 2
W1: 3
W1:A
W2:A
W1:B
W2:B
----------
W2: 1
W2: 2
W1:C
W1:D
W2: 3
W2: 4
W2:C
W2:D
----------
In first example, two .Where()
is being "appended" and executed together at the end when .ToList()
is called with item "A" going through the pipe first then item "B", hence seeing "AABB" in the output, while in the second example, the .Where()
is executed every time if we call .ToList()
immediately afterwards, hence seeing "CD" and then "CD" again, output twice. Therefore, every time an Enumerable is converted to a List or an Array will cost one O(n) iteration over all items in the collection which will have performance impact when the collection is large.
Although we don't trend to write code this way calling .ToList()
in between LINQ calls but it would happen more frequent when we factor code into reusable methods that return List/Array
rather than IEnumerable
.
This however doesn't mean we should always operate on IEnumerable
. As mentioned by towlson, that operations such as .Count()
will cause iteration over the collection while a List or Array would have this info pre-calculated and therefore converting to List/Array
would be more efficient if you plan to call .Count()
multiple times. This is also why there is a .Count
property on a List rather than a .Count()
method to count it.
IEnumerable
is a general-purpose interface that is used by many classes, such as Array
, List
and String
in order to let someone iterate over a collection. Basically, it's what drives the foreach
statement.
IList
is typically how you expose variables of type List
to end users. This interface permits random access to the underlying collection.
This is an old post, but still thought of replying. IEnumerable is a behavior while Array is a data structure(Contiguous collection of elements with fixed size, facilitating accessing elements by indexes) . When an Array implements IEnumerable, it is supposed to depict IEnumerable inherent property also (of facilitating iteration over the collection).
If someone is reading this recently, IEnumerable
could be lazy, so you'll have to be careful if your iterator has business logic within it:
public IEnumerable<int> GetTwoInts()
{
Console.WriteLine("1");
yield return 1;
Console.WriteLine("2");
yield return 2;
}
public void Something()
{
var twoInts = GetTwoInts();
}
Using the example in the answers, both Console.WriteLine
s will not be called until twoInts
are "used", i.e. used in a foreach-loop, or ToArray
is called, which might be unintuitive.