fastbyte22
fastbyte22

Reputation: 66

How to make this IEnumerator generic in a proper way?

Ive got a following coroutine in unity:

IEnumerator DoActionUponReachingRangeCoroutine(Range range, Delegate action, params object[] args)
{
    yield return new WaitUntil(() => range.isPlayerInRange);
    action.DynamicInvoke(args);
    yield return null;
}

As you can see it can execute some action when range.IsPlayerInRange condition is met. I would like to do exactly the same thing but with providing my own condition every time I call this method, not with isPlayerInRange hard-coded. Do I just provide a delegate instead of range?

Upvotes: 0

Views: 83

Answers (2)

RobloxHero
RobloxHero

Reputation: 55

Look up c# reflection to create a clone of your function if you want to write your function once, but replicate it separately.

Get method info

MethodInfo mInfo = typeof(Program).GetMethod("SlotClick");  // inside the loop!!

https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo?view=net-8.0

Then make a generic method changing the params to a type or types as needed. The docs explain,

Substitutes the elements of an array of types for the type parameters of the current generic method definition, and returns a MethodInfo

Below you’ll see my example building UI buttons and switching on a type.

https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo.makegenericmethod?view=net-8.0#system-reflection-methodinfo-makegenericmethod(system-type())

public class ShieldButton {
    public string ButtonImg;
    public void activateSheild() {
       // ...
    };   
}

public class Button {
    // this is the method we will get the method info of!
    // we are going to change the method parameters in the for loop when cloning.
    // then we will invoke the cloned method..
    public void click<T>(T Type) {
        switch (typeOf(Type)) {
            case ShieldButton: { Type.activateShield() };
        }
    }
}

public class CreateMenu {
    public void InitiateMenu()
{
    for (int i = 0; i < inventorySize; i++)
    {
        MethodInfo mInfo = typeof(Button).GetMethod("click"); // Grab the class and method!
        MethodInfo newInfo = mInfo.MakeGenericMethod(typeof(ShieldButton)); // make a clone of the method changing the parameter type to our ShieldButton Class!
        ShieldButton shieldButton = new ShieldButton(); // Create a new version of the shield button class
        newSlot.GetComponent<Button>().onClick.AddListener(newInfo.Invoke(null, shieldButton)); // Invoke the new methodInfo passing in the class to switch on!!!!
    }
  }
}

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1063864

WaitUntil takes Func<bool>, so you can simply pass that down:

IEnumerator DoActionUponCoroutine(Func<bool> predicate, Delegate action, params object[] args)
{
    yield return new WaitUntil(predicate);
    action.DynamicInvoke(args);
    yield return null;
}

You might also prefer Action for the callback, which is much more efficient than DynamicInvoke:

IEnumerator DoActionUponCoroutine(Func<bool> predicate, Action action)
{
    yield return new WaitUntil(predicate);
    action();
    yield return null;
}

There is also a way of using generic TState state and an Action<TState> callback to avoid capture contexts, but... that's a more advanced scenario.

Upvotes: 3

Related Questions