Problem storing a coroutine in IEnumerator type variable - only works once?

public class Main : MonoBehaviour
{
IEnumerator CoRef1;

    void Start()
    {
        CoRef1 = MyCo();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            StartCoroutine(CoRef1);
        }
    }

    IEnumerator MyCo()
    {
        yield return null;
        Debug.Log("MyCo");
    }
}

When I press “A”, only the first time will call MyCol(). Why is this?

Thank you!

When you create the Coroutine by calling the function that’s returning an IEnumerator, you’re creating an instance of a compiler generated class that is internally implemented as a state machine. As the coroutine runs it transitions between states each time you execute a yield statement. You can find more details on that using Google.

StartCoroutine just takes the coroutine instance and puts it in some internal list that Unity iterates over calling MoveNext on the IEnumerator. It doesn’t really know anything about what state the coroutine is in because those things are all handled by the compiler generated class.

So once the Coroutine runs to completion, the internal state will never change. When you pass that same instance to StartCoroutine and Unity eventually calls MoveNext, it’s already in its final state and so it won’t do anything.

IEnumerator has a Reset method that resets the enumerator back to its initial state. You should be able to call this on your instance before passing it to StartCoroutine again. According to the documentation this particular method is optional and can simply throw a not supported exception, so it may not work. If so, it’s probably possible to use reflection in some way to get access to the internal class state and manually change properties to reset the object.

Or you can just not try to keep a single instance around. Do you have a compelling reason for not just calling StartCoroutine(MyCo())?

This is a very subtle issue.

In a sense the actual answer to your question is that you’d have to call it again.

There is no reason you would ever want to do what is described, but if for some reason you wanted to,

you have to do this:

 CoThing1 = MyCo();

every single time, you want to do this:

StartCoroutine(CoThing1);

Note, I have renamed it from CoRef1 to CoThing1, since it is not a reference. It is the actual “output” of having run MyCo().

Note that this function:

public void CompletelyPointless()
  {
  IEnumerator x = MyCo();
  StartCoroutine(x);
  }

is, I believe, precisely the same as just calling StartCoroutine(MyCo);

So as I say:

you would have to do this CoThing1 = MyCo(); every single time you want to do this: StartCoroutine(CoThing1);

But to be clear, you would never ever want to do that, and should never ever do it. This is purely theoretical. To add to the confusion there are some code fragments on the net of beginners who do not know what a coroutine is typing code like that: never do it for any reason.

Alternately – within the context of this theoretical investigation – it’s possible you can use the possibly available Reset function DaveC mentions.

(Note however: that is an exceptionally subtle thing. You’d then be running the coroutine in question “as the game was then” (I think), not “as it is now”. I also have no idea what happens if you for example “change” (or indeed Reset) CoThing1, while it is possibly already running or even how you could know that. It is absolutely inconceivable you would ever want to do anything so malformed and non-explicit - anything you need to achieve like that can be achieved absolutely trivially as a matter of course in normal everyday patterns. This is purely a theoretical issue, never do it.)


Finally (thanks to StackOverflow!) here’s how you actually do

#What was probably wanted, or what is likely wanted by anyone googling here…

It’s this simple…

public class MultiStuff:MonoBehaviour
	{
	private delegate IEnumerator CoroutineDelegate();
	private CoroutineDelegate crt;
	
    private IEnumerator CoroutineA()
		{
		yield return null;
		Debug.Log("1");
		yield return null;
		Debug.Log("2");
		yield return null;
		Debug.Log("3");
		}

    private IEnumerator CoroutineB()
		{
		yield return null;
		Debug.Log("a");
		yield return null;
		Debug.Log("b");
		yield return null;
		Debug.Log("c");
		}

    public void Start()
		{
		crt = CoroutineA;
		
		// that is perfectly safe and 
		// works "as you'd think".
		// no prob calling more than once,
		// simultaneously etc etc.
		
		StartCoroutine(crt());
		StartCoroutine(crt());
		StartCoroutine(crt());
            
		crt = CoroutineB;
		StartCoroutine(crt());
		StartCoroutine(crt());
		}
	
	}

Hope it helps someone or is of interest.