Dgdas
Dgdas
Published
January 22, 2017
Skip to content
Tutorials
Unity
Shader
Python
Arduino
Machine Learning
Maths for Gamedev
Introduction
Part 1. Implementation
Part 2. The yield Statement
Part 3. Limitations
Conclusion
Introduction
If you are familiar with C#, chances are you might have used the List class. Like
most of the modern data structures available in .NET, the elements within a List
can be iterated in many way. The most common uses a for loop and and index i to
access the elements sequentially.
Implementation
Classes that can be iterated using foreach make such a behaviour possible by
implementing the IEnumerable interface (MSDN). Inside, it contains a method called
GetEnumerator that must be used to create and return an iterator. Like the name
suggests, iterators are data structures that can be iterated upon. They implement
the interface IEnumerator (MSDN), which provides an API to iterate over a sequence
of element. IEnumerator contains:
MoveNext: A method that forces the iterator to fetch the next element in the list.
It returns true if there is a next element; false if the sequence has terminated.
Current: This getter is used to return the current element of the iterator.
After having understood how an iterator class is implemented, it�s easy to see how
those two code snippets are equivalent:
// Foreach
foreach (int i in list)
Debug.Log(i);
// Iterator
IEnumerator<int> iterator = list.GetEnumerator();
while (iterator.MoveNext())
{
int n = iterator.Current;
Debug.Log(n);
}
1
2
3
4
5
6
7
8
9
10
11
// Foreach
foreach (int i in list)
Debug.Log(i);
// Iterator
IEnumerator<int> iterator = list.GetEnumerator();
while (iterator.MoveNext())
{
int n = iterator.Current;
Debug.Log(n);
}
The yield Statement
Iterators are awesome. However, they are a pain to code. Recording the position of
the current object and moving it at each subsequent call of MoveNext is not the
most natural way to iterate over a sequence. This is why C# allows a more compact
way to write classes that are compatible with foreach. This is done thanks to a new
keyword: yield.
Let�s imagine that we want to create an iterator that produces the elements , 1, 2,
and 3. We can either create a class that implements the IEnumerable interface,
instancing an IEnumerator that uses MoveNext and Current to produce the desired
list.
IEnumerator<int> OurNewEnumerator ()
{
yield return 0;
yield return 1;
yield return 2;
yield return 3;
}
1
2
3
4
5
6
7
IEnumerator<int> OurNewEnumerator ()
{
yield return 0;
yield return 1;
yield return 2;
yield return 3;
}
The compiler will take this piece of code and convert it in a proper IEnumerator.
With this new syntax, is incredibly easy to loop over the object the sequence:
Conclusion
This post introduced the concept of foreach loop, as a safer and more elegant
approach to traditional index-based for loops. To sum up:
IEnumerator it = �
try {
}
finally {
if (it is IDisposable)
((IDisposable)it).Dispose();
}
Reply to 0xmarcin
Alan Zucconi
January 25, 2017
Oh thank you so much for the clarification!
I tried to make this as simple as possible. There are few aspects I have
intentionally ignore such as Reset and stuff. :p I didn�t want to get too many
concepts in the same tutorial! :p
Reply to Alan
Tom
January 25, 2017
Where did �bat� come from for �bat.Current�?
Reply to Tom
Alan Zucconi
January 25, 2017
Ooops!
Thank you for spotting that!
That was form a previous version of that snippet code!
Reply to Alan
Sylvain
January 26, 2017
You website feeds don�t work anymore. Well at least on Feedly they don�t
Reply to Sylvain
Alan Zucconi
January 26, 2017
Oh not sure why, sorry! ??
Reply to Alan
rich
June 5, 2017
can you talk a little about the memory implications of use foreach? thanks
Reply to rich
Alan Zucconi
June 5, 2017
Hey!
I didn�t want to go too much into those details for a very simple reasons.
Sometimes developers get very worried about micro-optimisations (such as foreach vs
for), and ending up writing much more code and losing the big picture. Since this
was a fairly basic tutorial, I didn�t want to scare developers too much!
I will talk about it though, don�t worry! ??
Reply to Alan
Bartosz Olszewski
August 3, 2017
�Indices like i and j can be easily swapped by mistake, and edge conditions are
sometimes hard to get right on the first try.�
That�s why I prefer using names like �enemyIndex� or �buttonIndex�. They are longer
than single-letter ones, but it is harder to make a mistake, and the code is easier
to read.
Reply to Bartosz
Write a Comment
WEBMENTIONS
Tutorial Series - Alan Zucconi August 3, 2017
�Indices like i and j can be easily swapped by mistake, and edge conditions are
sometimes hard to get right on the first try.�
That�s why I prefer using names like �enemyIndex� or �buttonIndex�. They are longer
than single-letter ones, but it is harder to make a mistake, and the code is easier
to read.