Search Unity

Why coroutines inherit from IEnumerator?

Discussion in 'Scripting' started by Koval331, Oct 18, 2019.

  1. Koval331

    Koval331

    Joined:
    Feb 3, 2019
    Posts:
    114
    How does inheriting from IEnumerator make the method to behave like a coroutine? This has to be somewhat related to the compiler, right?

    Code (CSharp):
    1.     IEnumerator Fade()
    2.     {
    3.         for (float ft = 1f; ft >= 0; ft -= 0.1f)
    4.         {
    5.             Color c = renderer.material.color;
    6.             c.a = ft;
    7.             renderer.material.color = c;
    8.             yield return null;
    9.         }
    10.     }
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    IEnumerator is the return type, it's not inheriting from it.
     
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Unity uses IEnumerators in a different/highly specialized way from their intended use, which is more along the lines of running through a list without fully blocking execution of all other code. It's a bit of a hack that is used this way because they're the only C# concept that allows you to stop code execution in the middle of a function, then resume it later.

    It's really not IEnumerator that carries out coroutines' magic, but rather StartCoroutine. The actual call to Fade will immediately run the code up to the first yield return, then stop. StartCoroutine then takes that "yield returned" function, and sort of shuffles it onto a pile of "things to worry about later". In the next frame, the MonoBehaviour will go to this pile and, once again, run all the code in it until it comes to the next "yield return" (which is why, in a stacktrace, you'll see "MoveNext" at the top of any coroutine code). It's Unity doing all this work, not the IEnumerator type itself; the IEnumerator is just the foot in the door to allow this kind of interrupted code.
     
    Joe-Censored and Koval331 like this.
  4. Koval331

    Koval331

    Joined:
    Feb 3, 2019
    Posts:
    114
    Aha! So it's the StartCoroutine who decides whether to call a method or not, and the "IEnumerator / yield" is used just because there is no other way to stop the method execution! That's smart :)

    For anyone in the future, here is a quick example that may explain it even further:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CorSample : MonoBehaviour
    6. {
    7.     // Start is called before the first frame update
    8.     void Start()
    9.     {
    10.         Numbers numbers = new Numbers();
    11.         foreach (int n in numbers)
    12.         {
    13.             Debug.Log("n: " + n);
    14.             Debug.Log("and i am here: " + n);
    15.         }
    16.         Debug.Log("End");
    17.     }
    18. }
    19.  
    20. class Numbers
    21. {
    22.     public IEnumerator GetEnumerator()
    23.     {
    24.         for (int i = 0; i < 6; i++)
    25.         {
    26.             yield return i * i;
    27.             Debug.Log("so i am here: " + i);
    28.         }
    29.     }
    30. }
    31.