public abstract class YieldEnumerator : IEnumerator<YieldInstruction>
A custom implementation of IEnumerator<YieldInstruction> which you can derive from
in order to implement
reusable enumerators. By default, when you have a method that returns IEnumerator
(or IEnumerator<YieldInstruction> in our
case), with yield statements in the method body, the .Net compiler will auto generate
a class behind the scenes that serves as a
state machine. When your method is called, this class is created anew, generating
garbage.
As an alternative, you can create your own implementation of IEnumerator that can
be reused, however the drawback is that you need
to implement all of the state machine logic yourself, as well as create the fields
to store any arguments that are passed into the
original method. I cannot help with the former issue, however the YieldEnumerator
and other generic alternatives of it provide
a means of taking care of some common coding necessities, such as the fields for storing
the arguments and nulling out these fields
after the enumerator is finished.
Note that you also need to ensure that the enumerator is only in use by one caller
at a time. You can use the InUse property for this, as
it will be set to true when PrepareForIteration is called, and set to false once MoveNextImplementation
returns false. Typically you
will run into this issue if using multiple Worlds, however you should take the issue
into account and create a new enumerator if
your current enumerator is in use. The ClearFields and ResetImplementation methods
are called automatically within the MoveNext
method once MoveNextImplementation returns false. The Reset method is not implemented.
Every time the enumerator is called for, PrepareForIteration should be called to ensure
correct functioning, whether the enumerator
is a reused object or a new object.
Only a single YieldEnumerator class is shown here, however the DeepSpaceLabs.SAM namespace
contains multiple generic YieldEnumerators.
The generic parameters should match the input parameters of your method that returns
IEnumerator<YieldInstruction>. If you cannot find
a class that contains the generic parameters you need, you can create your own class
that derives from YieldEnumerator. This class should
have fields matching the method parameters your method takes as arguments, which you
will need to set. You should also null out any
reference fields by implementing the NullRefFields method, which is called after once
the MoveNextImplementation returns false.
You will not be able to access the parent instance fields or methods from within MoveNextImplementation
using this as a base for your custom enumerator. If you need this
ability, please derive from the YieldEnumeratorWithParent (or one of its child generic
classes) instead. When doing this, make sure
to assign the Parent property. If you are reusing the same enumerator with multiple
parent different instances, you can assign the Parent
before the enumerator is used, and null it out in your NullRefFields method.
Name | Type | Description |
---|---|---|
Current | YieldInstruction |
The current YieldInstruction. By default it is null, which wil yield for a single frame. If that suits your code, you don't need to set it to anything else, however if you want to yield on a different time frame, set this value before returning true from your MoveNextInternal method. |
InUse | bool |
Tells you whether this enumerator is in use by some caller. |
MoveNextCalls | int |
How many times MoveNext has been called by whatever is using this enumerator, before
the current MoveNext.
Will be 0 the first time MoveNext (and thus MoveNextImplementation) is called, then
1, then 2, and so on. The value is
also reset to 0 in the Reset method which will be called after SAM is finished using
the enumerator, so that the next time it is
used, it will be 0 for the first call.
|
Phase | int |
In general, enumerators make use of state machine logic, which operates in phases (Phase 1, do this, phase 2, do that, etc.). This is a convenience property since most enumerators will use a phase, so this avoids you needing to type out a phase variable. You do not need to use it. The Phase is reset to DefaultInitialPhase when PrepareForIteration is called, which is 1 unless overriden. After that, you should set the phase yourself in your MoveNextImplementation. By default the Phase will always start at 1, however you can override this behavour by setting it to a different value in your PerformAdditionalIterationPreparation method. |
Not implemented
Not implemented
public bool MoveNext()
Iterates the enumerator, which first calls MoveNextImplementation, then increments MoveNextCalls, and finally calls Resets the enumerator if MoveNextImplementation returned false (this sets InUse to false, MoveNextCalls to 0, and then calls the ClearFields and ResetImplementation methods).
bool
True if the enumerator was advanced to the next Yield Statement (and thus there is
more code to exeucte),
false if not. Current should only be called when this returns true. Once MoveNext
returns false, as the caller you should not
use it again. Also note, Current will automatically be set to null once it returns
false, so you don't have to.
Type | Condition |
---|---|
InvalidOperationException | Thrown when this method has been called before PrepareForIteration is called. |
public abstract bool MoveNextImplementation()
Override to provide an actual implemntation of the MoveNext method, which advances the enumeration. Under normal circumstances, you would put in here whatever you would have put in the MoveNext method, however the MoveNext method is reserved to perform some additional actions.
bool
True if the enumerator was advanced to the next Yield Statement (and thus there is
more code to exeucte),
false if not. Current should only be called when this returns true. Once MoveNext
returns false, the caller you should not
use it again until calling the method that returned the enumerator again.
public virtual void NullRefFields()
Called automatically once MoveNextImplementation returns false, you need only worry
about implementing this method if
not deriving from one of the Generic implementations of YieldEnumerator and only when
you have reference fields that should be
nulled out after the enumerator has finished iterating. Note, the method is implemented
by the generic types, which is why you
don't need to worry about it if deriving from one of them. The generic types that
have a Parent will also null out the parent,
which is why the PrepareForIteration method needs to pass in the Parent instance each
call.
If you are deriving from one of the generics, any reference fields indicated by the
type parameters will be nulled out
automatically (and also the Parent). Any addditional fields you want to null out should
be nulled out in your ResetImplementation method.
Also note that this method is called after ResetImplementation, so you can still access
the references if needed in order to
perform the reset (although note this is unlikely).
public virtual void PerformAdditionalIterationPreparation()
You can override this to perform additional initialization each time the enumerator will be used. When PrepareForIteration is called (automatically when you are deriving from one of the generic YieldEnumerators and you call the PrepareForIteration method with the arguments for your fields, or manually if not deriving from one of those but instead from YieldEnumerator or YieldEnumeratorWithParent directly), this is called by it.
public YieldEnumerator PrepareForIteration()
Prepares the enumerator for iteration. Should be called before returning the enumerator
to whatever caller is going to
iterate over it. If you are deriving from YieldEnumerator or YieldEnumeratorWithParent
directly, you should call this method
manually before returning the enumerator to the caller. This method will call PerformAdditionalIterationPreparation,
so if you are
deriving from one of the generic YieldEnumerators, which calls PrepareForIteration
automatically, and you need to
perform some sort of setup or initialization each time the enumerator is about to
be used, override
PerformAdditionalIterationPreparation.
If you are deriving from one of the generic implementations of YieldEnumerator, do
not call this method yourself, instead call
the PrepareForIteration method that uitilizes the generic types you are making use
of as parameters.
Also note that PerformAdditionalIterationPreparation is called before the DefaultInitialPhase
is queried, so you can setup data that
manipulates the value of DefaultInitialPhase if you so choose.
Returns itself, which is just intendend to make one line initialization and returns
possible
public virtual void ResetImplementation(
Called automatically by the base YieldEnumerator once MoveNextImplementation returns false. You don't have to to implement this; only do so if you have some cleanup that needs to be performed outside of nulling reference fields. Note that nulling reference fields, including the Parent if deriving from YieldEnumeratorWithParent, is done after this method is called, so you can still access those fields to perform the reset if needed.