-
Notifications
You must be signed in to change notification settings - Fork 88
When should I use IEnumerable?
I'm just writing this single article just as a way to redirect any questions about IEnumerable<T>
since while initially may seem insanely simple in reality it is far from that.
Use IEnumerable whenever you don't have to return a whole list, since IEnumerable exists purely as a lazy list (effectively) which means that you generate the next item when you need it, which makes it bad in some cases due to the need to go and generate every element before you use it but in most cases makes it insanely memory efficient and if you were going to generate the whole array anyway makes it even more efficient.
Such as in ContextMenu when you have a list of options that you want to return (in this case a list of actions) it is better to use a IEnumerable, since you cut down on having to generate an entire list every single time.
Linq utilises IEnumerables but often the context is different (though in reality it of course uses yields, just abstracted away).
If you ever use what I will refer to as a 'de-lazy' command which effectively turns the lazy structure into a real structure (an example being the ToList()
function, then think about using a different structure then Linq) the reason why a different structure is better is down to the fact that it wastes memory and space, since every yield is instantly put into a list it has to create new references and has to garbage collect the old ones, it also requires the entire list to be created before continuing so doing something like foreach (Item item in GetItems().ToList())
will in reality generate the entire list then iterate through it which wastes performance and is much slower than if you just returned a list in the first place. There are a few other cases which De-Lazy and the way to identify them is to say 'What does this give me', or rather 'Can I say var x = Y(z)
where z is the IEnumerable and not require the IEnumerable to be evaluated before it runs Y?
A tricky example that sometimes gets people is the following;
public void A(IEnumerable<int> iterable) {
foreach (int i in iterable) {
Debug.Log(i);
}
}
public void B(List<int> iterable) {
foreach (int i in iterable) {
Debug.Log(i);
}
}
public void C(int[] iterable) {
foreach (int i in iterable) {
Debug.Log(i);
}
}
public void D(IEnumerable<int> iterable) {
foreach (int i in iterable.ToList()) {
Debug.Log(i);
}
}
public IEnumerable<int> Generate() {
for (int i = 0; i < 100; i++) {
yield return i;
}
}
A(Generate()); // Ex 1
B(Generate().ToList()); // Ex 2
C(Generate().ToArray()); // Ex 3
D(Generate()); // Ex 4
IEnumerable<int> a = Generate();
A(a); // Ex 5
List<int> a2 = Generate().ToList();
A(a2); // Ex 6
Which ones ruin the lazy evaluation? The answer is that Example 2, 3, 4, and 6 ruin it. Only Example 1 and 5 actually utilise the Lazy Evaluation.
The reasons why?
- Example 1: It doesn't ruin the lazy evaluation and is a very simplistic and generic way to use IEnumerables.
- Example 2: It fails the lazy evaluation because of the conversion to a list.
- Example 3: It fails the lazy evaluation because of the conversion to an array.
- Example 4: It fails the lazy evaluation because of the conversion to a list (inside the function).
- Example 5: It doesn't ruin the lazy evaluation because the assignment won't 'de-lazy'.
- Example 6: It fails the lazy evaluation because of the conversion to a list.
- Features
-
Roadmap
- Milestone 0.3
- Milestone 0.2 (reached)
- Milestone 0.1 (reached)
- Gameplay
- Frequently Asked Questions