coroutines - get compile (or runtime) warning when called as a normal method ?

In the following code example, the coroutine is being called as a normal method - ie, not from a yield in another coroutine and not via StartCoroutine(). the result is that none of the code in the coroutine is executed.

i would love to get a runtime error in this situation, or even better, a compile-time error.

anybody know how to do that ?

    void test() {
      Debug.log("before calling coroutine");  // prints
      myCoroutine();
      Debug.log("after calling coroutine");  // prints
    }

    IEnumerator myCoroutine() {
      Debug.log("inside the coroutine"); // does not print
      yield break;
    }

The problem here is that the yield keyword is syntactical sugar that makes that method you wrote unlike other methods. You are returning an IEnumerator object and by just calling the method, nothing is operating on that object.

Jon Skeet wrote a chapter on Iterators that talks about this, but here’s the most important line:

-None of the code [you] wrote in [your IEnumerator] is called until the first call to MoveNext.

In your example, nothing calls .MoveNext() on your IEnumerator (like StartCoroutine() would) and so you never reach that Debug.Log() inside the method. Using the syntactical sugar of the yield keyword, the compiler creates a behind-the-scenes state machine for your method as an object. All you’ve done by calling the method is create that IEnumerator object. The code that you actually want to execute is wrapped inside that object and inaccessible unless something calls MoveNext(). Because the compiler itself does the wrapping, there’s nowhere inside the IEnumerator method you wrote to insert some kind of logic to throw an exception or error if MoveNext() doesn’t get called.

I don’t think what you’re asking for is possible, but if someone knows something I don’t, by all means, please educate!


You can, however, create concrete implementations of the IEnumerator interface to avoid calling the IEnumerator wrapper as a method outside of StartCoroutine():

using UnityEngine;
using System.Collections;

public class WaitOneHundredFrames : IEnumerator
{
    private int _position = -1;

    public object Current
    {
        get
        {
            return _position;
        }
    }

    public bool MoveNext()
    {
        // Put whatever logic would be in your normal IEnumerator method here
        // In this case, we're just incrementing a number
        _position++;
        Debug.Log(_position);
        // When you want the coroutine to stop, return false
        // In this case, we want to return false when our number reaches 100
        return _position < 100;
    }

    public void Reset()
    {
        _position = -1;
    }
}

Pass an instance of this class as an argument inside of StartCoroutine()

using UnityEngine;
using System.Collections;

public class RunMyIEnumerator : MonoBehaviour
{
    private IEnumerator _enumerator = new WaitOneHundredFrames();
	private void Awake()
	{
        StartCoroutine(_enumerator);
	}
}

This method is obviously more work, but you get a compile time error if you just call _enumerator() (because it is a class instance, not a method).

Try changing yield break to yield return null;

if that doesn’t work,

use:

StartCoroutine("myCoroutine");