IEnumerator

Hi, I am trying to learn coroutines and noticed that they start with IEnumerator. I am trying to figure out what this actually is. Searching google has not helped, as most answers just confused me more. I am not very familiar with C# however I have done some C++.

I noticed while searching there is IEnumerator, and IEnumerable. Is IEnumerable, just a enum in C++. If so, what is an IEnumerator, where does that fit in.

Thank you for any help.

Edit: Also, when reading googles answers, people keep talking about interfaces, is that equivalent to a class in C++

Coroutines utilize a feature of C# called ‘iterators’. Note that ‘iterators’ weren’t really designed with Coroutines in mind… Unity is just taking advantage of how it works.

Iterators are really designed to create a sequence on the fly.

This sequence is represented as an IEnumerable. IEnumerables are objects are objects that can be iterated over. Collections are the most obvious example of this. A List is an IEnumerable, an array as well. HashSet, Dictionary, etc.

All an IEnumerable has on its interface is a method ‘GetEnumerator’. ‘GetEnumerator’ returns an IEnumerator object. This is the object that actually does the iterating (foreach loops are just syntax sugar around the MoveNext() method of the IEnumerator).

The iterator function you write for the Coroutine returns an IEnumerator to facilitate this MoveNext() which is used by the Coroutine to well… move through the coroutine to each yield statement.

1 Like

Coroutines in a nutshell:

Splitting up larger tasks in coroutines:

Coroutines are NOT always an appropriate solution: know when to use them!

Our very own Bunny83 has also provided a Coroutine Crash Course:

https://answers.unity.com/questions/1749615/coroutines-ienumerator-not-working-as-expected.html?childToView=1749714#answer-1749714

2 Likes

No, it’s more of a contract… I suppose an abstract class would be the closest C++ analogy.

Interfaces play extremely well in Unity with MonoBehaviours / ScriptableObjects. It’s incredibly powerful once you start to think of the use cases.

Using Interfaces in Unity3D:

https://discussions.unity.com/t/797745/2

https://discussions.unity.com/t/807699/2

Check Youtube for other tutorials about interfaces and working in Unity3D. It’s a pretty powerful combination.

1 Like

No :slight_smile: It has nothing to do with that at all. Since you are at least a bit familiar with C++, you may know the concept of iterators? An IEnumerator is just an iterator in a nutshell. An IEnumerable is just an object that can provide an IEnumerator, that’s all. For more details read through my coroutine crash course that Kurt has already linked above :slight_smile:

2 Likes

Thanks all for the replies. I took a few days to try and understand everything. i think I have the basics of what is happening now. One question I have though is, in one of the posts I read it says

“However since the C# compiler can magically turn our linear code into a statemachine that splits our code apart at the points where we yield a value, we can actually use that “method to statemachine” magic to implement coroutines.”

Does this mean a state machine is created at each yield statement. So if there are 3 yield statements in a coroutine, it would create 3 state machines?

No :slight_smile: The coroutine itself is a statemachine. Each time MoveNext is called it would advance to the next “state”. As I explained in my post, Unity takes care of advancing the statemachine when it’s “due”. When your coroutine / statemachine is continued depends on the value that you yield.

I gave a concrete example in my coroutine crash course what the compiler actually generates. In the section “The yield keyword” I show an example of a very small iterator method and below what the compiler actually creates for you behind the scenes. Just to be clear here, the code I’ve shown is literally what is generated. So when you call your coroutine, the method actually creates an object instance of that internal class which is returned by that method. Nothing else happens when you call your coroutine method. When starting a coroutine we actually pass this object to the StartCoroutine method and the Unity coroutine scheduler will store this instance internally and start “iterating” this statemachine.

1 Like

Technically you could create a coroutine without the yield keyword by implementing such a state machine yourself. Passing such an object to StartCoroutine would have the same effect. However it would be cumbersome to create such a statemachine manually. The yield keyword is what makes coroutines that easy to use.

Just as an example, have a look at this class

public class MyStatemachine : IEnumerator
{
    int counter = 0;
    public object Current {get; set;}
    public bool MoveNext()
    {
        if (counter++ < 5)
        {
            Debug.Log("Step " + counter)
            Current = new WaitForSeconds(1);
            return true;
        }
        return false;
    }
    public void Reset () { }
}

Try a class like this, just for demonstration purposes. You can do this:

StartCoroutine(new MyStatemachine());

As a result Unity will start calling MoveNext on that object. Whenever MoveNext returns true, a new “value” is available in the Current property. Unity will read that value, recognises the WaitForSeconds object and stores the object internally and re-schedules the object in 1 second. So after 1 second has passed, Unity will again call MoveNext and again checks the Current property and decides based on that value what to do next. Once MoveNext returns false, the iterator has finished and the coroutine ends.

Again, the whole IEnumerator / IEnumerable and yield mechanic are actually meant to produce a sequence of values that can be iterated. Unity uses this mechanic to implement coroutines. The “values” that your coroutine produces just communicate to the scheduler what to do next while your actual code that you care about is just additional code in between. This example object I just posted would be similar to a coroutine that looks like this

IEnumerator MyCoroutine()
{
    for (int i = 0; i < 5; i++)
    {
        Debug.Log("Step " + i);
        yield return new WaitForSeconds(1);
    }
}

This does literally the same thing.

1 Like