- Home /
Get IEnumerator from name or convert MethodInfo to IEnumerator
Title says it all: I am stuck on how I can return/store an IEnumerator, when I can only access its name. So I have a string, which is its name, and I want to end up with an IEnumerator.
I already know it exists, as I am getting the name of the coroutine from MethodInfo, but I need to store it for later use, and not start/invoke it!
EDIT: More info in comments
Answer by wibble82 · Dec 22, 2015 at 03:39 PM
Hi there
OK! After lots of comments below, I'm going to update this answer. What I originally wrote is still pretty relavent, so here it is in italics:
Calling the function returns the IEnumerator that unity uses to run your coroutine. You can call functions using method info using 'invoke': https://msdn.microsoft.com/en-us/library/a89hcwhh(v=vs.110).aspx So if you have the MethodInfo, you can call the function whenever you want. This will return an IEnumerator object. The IEnumerator object is simply the C# means to provide 'yield' functionality - i.e. a function that can execute in steps, yielding periodically. When you pass the IEnumerator to StartCoroutine, this simply hands it off to unity. Internally it creates a coroutine object, from which it repeatedly calls the enumerator until it finishes.
Now, onto the meat.
After lots of back and forth, my understanding of your problem is that, through whatever means, you have a function (you called ClassC) that currently has (or at least can be provided):
The name of a function from another class that returns an IEnumerable
Through reflection, you have got the MethodInfo for that function
You also say you have the 'script' it is on. Hopefully by that you mean the 'instance' of the script?
You have these 3 bits of info, because you want to extend the capabilities of unity's coroutine system to allow for pausing.
Your approach to this, which seems perfectly reasonable, is to create 1 generic unity coroutine that 'wraps' the calling of a 'coroutine function' provided by another class. Your generic coroutine function has pausing built in. This was your example, which I've not tested but seems reasonable:
public IEnumerator RunRoutineManually()
{
while (true)
{
if (!routineIsPaused)
{ //Imagine this is a bool controlled by ClassB
if (routineHasStopped)
{ //This aswell
routine.Reset();
Destroy(this.gameObject);
yield return false;
}
yield return null; //Coroutine is paused!
}
routine.MoveNext();
yield return routine.Current;
}
}
The question you are asking is: So I have my function name, my MethodInfo and my 'script', but my 'generic coroutine' needs an IEnumerator. Where the do I get it?!
So, to achieve your goal, all you actually need are:
The MethodInfo of your 'coroutine function'
The instance on which you want to run the coroutine - i.e. if the function is defined as part of ClassB, you will need the instance of classB on which you wish to run it. Hopefully this is what you meant by 'I have the script'
To explain things in detail, first I want to make sure you understand some bits and pieces (sorry if you already know this stuff!):
First, I've being saying 'coroutine function' so far, but there's really no such thing! This, is not a coroutine:
public IEnumerator YieldReturnSomeBooleans()
{
yield return false;
yield return false;
yield return false;
yield return false;
yield return false;
yield return true;
}
And neither is RunRoutineManually(). They are Enumerators. i.e. they are functions that the .net library uses to implement stuff like 'foreach' loops. Their critical property is that they can 'yield', return a result, and then be told to 'carry on', all by calling functions on the IEnumerator that they return. This I think you already understand.
Entirely independently, unity also implements a system called 'coroutines', which some languages would call 'fibres'. This system allows you to create functions that run independently of the stuff like 'Update', and have the ability to yield, then carry on next frame.
Naturally, the engineers at Unity decided the best way to expose coroutines to users was through Enumerators, because they provide the mechanism for writing a function that can yield, then be told to carry on. As a result we end up with the function:
StartCoroutine(IEnumerator coroutine)
There is also a version that takes a string, but that's just a utility and I want to ignore it for now!
You use the unity system by calling your Enumerator function, and passing the IEnumerator it returns into StartCoroutine. From there Unity takes over, calling 'MoveNext' on the enumerator you provide it once per frame (or in other ways depending on what you return from it).
So:
StartCoroutine(YieldReturnSomeBooleans());
Could be written:
IEnumerator myenum = YieldReturnSomeBooleans();
StartCoroutine(myenum);
Or if the coroutine function was part of another object, you could write:
IEnumerator myenum = otherobject.YieldReturnSomeBooleans();
StartCoroutine(myenum);
Similarly, you could store that 'myenum' value somewhere, pass it around, and later on call StartCoroutine.
Now, assuming you have your methodinfo and the instance on which you want to call it, you can do exactly the same as above. However instead of calling the YieldReturnSomeBooleans function directly, you can 'invoke' it via reflection:
//I assume you have these somewhere?
MethodInfo mymethodinfo = ?;
object myobject = ?;
//invoke the method that we know returns an IEnumerator on myobject, passing an array of 0 paramaters
IEnumerator myenum = (IEnumerator)mymethodinfo.Invoke(myobject, new object[]{});
//now start it as usual
StartCoroutine(myenum);
So, going all the way back to your original function:
public IEnumerator RunRoutineManually()
{
//these are the values you presumably are storing in 'ClassC' now - not 'routine'
MethodInfo mymethodinfo = ?;
object myobject = ?;
//we get 'routine' through invoke
IEnumerator routine = (IEnumerator)mymethodinfo.Invoke(myobject, new object[]{});
//everything else is just as you wrote it
while (true)
{
if (!routineIsPaused)
{ //Imagine this is a bool controlled by ClassB
if (routineHasStopped)
{ //This aswell
routine.Reset();
Destroy(this.gameObject);
yield return false;
}
yield return null; //Coroutine is paused!
}
routine.MoveNext();
yield return routine.Current;
}
}
I hope that very long explanation was for the right thing. If not, hopefully it gets you on the right path.
-Chris
p.s. I haven't tested that code, so there might be a few compile errors. It's along the right lines though.
Hey Chris,
So I think I should elaborate a bit more on why I am doing this. I want to pause a coroutine, generically/any kind of/non-specified, while there is another non-specififed coroutine running.
I have two scripts. One with a single class which contains the coroutines, and one with a namespace which includes two classes. The first class figures out which coroutine is supposed to be paused, and the second class which manually runs the coroutine.
This is whats inside ClassC, which is a separate class because ClassC must derive from $$anonymous$$onoBehaviour, while ClassB must derive from another custom class, let's call it ClassD.
class ClassC : $$anonymous$$onoBehaviour {
public IEnumerator routine;
public string itsName;
void Start() {
//TODO: Get routine from itsName
StartCoroutine(RunRoutine$$anonymous$$anually());
}
public IEnumerator RunRoutine$$anonymous$$anually() {
while (true) {
if (!routineIsPaused) { //Imagine this is a bool controlled by ClassB
if (routineHasStopped) { //This aswell
routine.Reset();
Destroy(this.gameObject);
yield return false;
}
yield return null; //Coroutine is paused!
}
routine.$$anonymous$$oveNext();
yield return routine.Current;
}
}
}
As you can see, I already am invoking the coroutine manually. But right now I only have the itsNames variable, and not the actual routine variable.
I also have the $$anonymous$$ethodInfo value of RoutineA, stored in ClassB, if this is necessary to access.
Its difficult to explain why, but you have to trust when I say that it has to work like this: FunctionB in ClassB returns RoutineA from ClassA, using $$anonymous$$ethodInfo. FunctionB then passes RoutineA as $$anonymous$$ethodInfo to FunctionC in ClassC.
Hi Lahzar
I'll confess I'm a little confused - that'll take some thinking through. What is your actual question though? Certainly my answer is how you can get the IEnumertor to start a coroutine using $$anonymous$$ethodInfo. Perhaps that's not what you actually want to know?
p.s. I would recommend removing the big [UNANSWERED] from the question - we know it isn't answered as it isn't highlighted as 'accepted' in green. Big capital letters never help anybody :)
-Chris
Hehe, the big [UNANSWERED] is just because Im paranoid people will think its one of those posts which has been answered, but OP, me, hasn't accept the answer anyways :S Will do, though, thanks.
I know its confusing! I find it really difficult to explain even the simplest of things, so me explaining this kind of advanced stuff has proven before to not get me anywhere, but I keep on trying!
I want to call a coroutines .$$anonymous$$oveNext and .Current methods, without actually having direct access to the coroutine! All I have is its name, what script it is in, and I get all this from a $$anonymous$$ethodInfo call:
BindingFlags bindAttr = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.OptionalParamBinding;
$$anonymous$$ethodInfo methodInfo = type.Get$$anonymous$$ethod(routineName, bindAttr);
//This is the methodInfo of RoutineA.
//This code is in ClassB.
If you go check my previous questions, you can see I previously asked for your answer, actually. But I have been starting a coroutine using $$anonymous$$ethodInfo for a while now, problem is I now want to PAUSE my coroutine, while another method/routine/whatever, is running. I have everything in order, except I don't have an actual reference to the routine I want to pause!
So that is what I need! A variable which is the IEnumerator I want. All I have to get it is: The routines name. The routines containing script and gameobject etc. The $$anonymous$$ethodInfo which is the routine, but not an IEnumerator.
Answer by jmonasterio · Dec 22, 2015 at 07:04 PM
Perhaps see:
http://twistedoakstudios.com/blog/Post83_coroutines-more-than-you-want-to-know
In the article it mentions:
Your coroutine can yield one of these in order to wait for another coroutine to finish before resuming execution.
Which sounds like what you said you want to do (in comments).
Hey man!
So I understand your confusion, as I am really bad at explaining. But pausing a coroutine is not what I want to find out how to do, because I can already do it!
I actually followed the twisted oak studios video tutorial, and blog, which really helped! If I had been able to pass the entire coroutine as a parameter, it would all work. But the coroutine is passed as a string! That is a big factor here! That is how I get its name.
What I want: I want to generically pause any coroutine from this other script.
What do I think I need: I need to access the coroutines .$$anonymous$$oveNext and .Current methods.
Why can't I already: I can't do this already because I don't have an IEnumerator variable. All I have is the routines name, what script it is located in, and a $$anonymous$$ethodInfo variable which is infact the coroutine... But not an IEnumerator. And I don't know how to convert or cast these types!
Thank you all for you feedback! Just ask if there is something about my question you don't understand, and I will try to elaborate futher! ~ Imre Angelo
Your answer
Follow this Question
Related Questions
Play animation then destroy gameOject C# "SOLVED" 1 Answer
wait a second before say path is valid? 2 Answers
Ienumerator wait for event 0 Answers
AnimationCurve as fixed time rather than portion of time? 1 Answer
Return result of the IEnumerator. 2 Answers