thathurtabit
thathurtabit

Reputation: 598

Unity C# Action Delegates - How to Create List<Action> with different parameter types?

I'm hoping for some help here (I'm new to C# and Unity).

I want to create a List of Actions that I can eventually loop over and clear / nullify during the OnDestroy lifecycle method call.

Anyhoo, I'll mark below what I'm trying to achieve and what the errors are:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Game : MonoBehaviour
{
    // Action delegate
    public static Action OnEvent1;
    public static Action<int> OnEvent2;
    public static Action<Vector2> OnEvent3;
    public static Action<Vector2> OnEvent4;
    public static Action OnEvent5;
    public static Action<bool> OnEvent6;

    private List<Action> ActionListToClear = new List<Action>() {
        OnEvent1, // all good
        OnEvent2, // Error as Action<int> is not Action
        OnEvent3, // Error as Action<Vector2> is not Action
        OnEvent4, // Error as Action<Vector2> is not Action
        OnEvent5, // all good
        OnEvent6  // Error as Action<bool> is not Action
    };

    ....

    private void OnDestroy()
    {
        foreach (Action action in ActionListToClear )
        {
            action = null;
        }
    }
};

I've also tried implementing a generic type into the List:

private List<Action<T>> ActionList = new List<Action<T>() { OnEvent1, OnEvent2... etc. };

but that results in:

The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)

Does anyone know the best way to create a List<Action> with different Action types? I.e. with and without parameters? Thanks!

Upvotes: 1

Views: 4623

Answers (2)

thathurtabit
thathurtabit

Reputation: 598

As a couple of kindly posters pointed out, the approach of adding all my Actions to a List to loop over during OnDestroy likely over-complicates the issue.

Instead the following does the trick:

private void OnDestroy()
{
    OnEvent1 = null;
    OnEvent2 = null;
    OnEvent3 = null;
    ...etc.
}

Upvotes: 0

Andy
Andy

Reputation: 13557

I'm going to take a stab at this. All the comments under your question are great. You have to think in a more object-oriented way. Here is an idea on how you could accomplish what you want and maybe get you thinking more outside the box. I'm not saying this is how you should do it, by any means. Just an idea of something you could work with.

So you define an interface, this is what all "Action" objects will have in common. You say in your question you just want a way to "Destroy" them, so we will force anything that implements it to also implement Destory()

public interface IAction
{
    void Destroy();
}

Now we create a "Base" action that can do 1 parameter that has a default implementation of what Destroy should do:

public abstract class BaseAction<T> : IAction
{
    public Action<T> Action { get; protected set; }
    public virtual void Destroy()
    {
        Action = null;
    }
}

Now we create another "Base" action that can do 0 parameters that has a default implementation of what Destroy should do:

public abstract class BaseAction : IAction
{
    public Action Action { get; protected set; }

    public virtual void Destroy()
    {
        Action = null;
    }
}

Then we create our actual events:

public class Event6 : BaseAction<bool>
{
    public Event6()
    {
        Action = new Action<bool>(x =>
            Console.WriteLine($"I just executed Event 6 with {x}"));
    }
}

public class Event7 : BaseAction
{
    public Event7()
    {
        Action = new Action(() =>
            Console.WriteLine("I just executed Event 7"));
    }

    public override void Destroy()
    {
        // do something special here
        base.Destroy();
    }
}

public class Event8 : BaseAction<int>
{
    public Event8()
    {
        Action = new Action<int>(x =>
            Console.WriteLine($"I just executed Event 8 with {x}"));
    }
}

Now we can create all the events we want

private Event6 _ev6 = new Event6();
private Event7 _ev7 = new Event7();
private Event8 _ev8 = new Event8();

public void Main(string[] args)
{
    // how you'd call the actions:
    _ev7.Action.Invoke();
    _ev6.Action.Invoke(true);

    // now remember, the `Action` member could be null if they were Destroyed... see below

    // you can then create an array of them because they all have "IAction" in common
    var lst = new List<IAction> { _ev6, _ev7, _ev8 };

    // you can also iterate over them and destroy them.
    lst.ForEach(x => x.Destroy());  
}

Upvotes: 1

Related Questions