Attach actions to scriptable object

Hi, first of all happy new year !

I am facing a problem with a powerup system, and don’t know what is the best way to approach it.
I’m building a small game (mobile app) where you can touch some power ups to launch differents upgrades in game (ex : double up score, …).
PowerUps values are stacked into a So :

[CreateAssetMenu(fileName = "Upgrade", menuName = "Settings/Upgrade")]
    public class UpgradeSettings : ScriptableObject
    {
        public string upName;
        public int amountStacked, price, duration;
        public Sprite icon;
        public bool active = false;
        public static Action<int> _event;

        [TextArea]
        public string quickDescription;



        /// <summary>
        /// Fire virtual method to manage amount of updates aviable && launch action attached to it
        /// </summary>
        /// <param name="upgradeSlot">Pass upgrade slot to refresh it with new values</param>
        public virtual void Use(UpgradeSlot upgradeSlot)
        {
            _event.Invoke(duration);
            amountStacked--;
            upgradeSlot.SetupUpgrade(upgradeSlot.upgrade);
        }
    }

As you see i have a virtual method who is launched by the “upgrade slot” (Ui button where the uprgade is attached to.

And there is my question
do you know a manner to attach individual action to each powerup i create using asset menu or do i need to make a callback passing the upgrade and then apply the effect of the differents upgrades
ex like this :

 public void ActiveUpgrade(Upgrade upgrade)
    {

        switch (upgrade.upName)
        {
            case "BigNet":
                StartCoroutine(BigNet(upgrade));
                break;
            case "Multiplier":
                StartCoroutine(Multiplier(upgrade));
                break;
            case "Freeze":
                StartCoroutine(Freeze(upgrade));
                break;
            case "ExtraLife":
                StartCoroutine(ExtraLife(upgrade));
                break;
        }
    }

Thanks for your replies and have a nice day

I don’t think there’s some general fits em all solution, but I will try to describe my own solution. What I do is that I have a ScriptableObject which defines a type of effect/powerup that can be applied to the character. It has an abstract method that instantiates a State object (pure class, not ScriptableObject) of that particular type of an effect. This state instantiation method is called by the character, when it has the effect added to it. The effect reference (the ScriptableObject) and its state are stored in the dictionary inside the character. This way everyone reuses the same instance of ScriptableObject (and can refer to it, compare, check if some particular effect is active on the unit) and the state, which is the character’s own object, which holds eg. the time left for the effect to go away, ammunition left etc.
Then it’s up to you how to implement the actual effect on the behaviour of an unit. The State classes, for example, could have some virtual methods like OnEnable and OnDisable that apply the changes to the character. It gets tricky when you are allowed to have multiple effects on a character at the same time, you don’t want them to override others’ changes. So one possible solution is to have a set of affectable variables and a strict order of effects. Whenever an effect is added or removed, reset the affectable variables (helps to have them in a struct) to the default values, then iterate over active effects and let them mofify the values they want, ideally by taking the previous value into account (eg. adding or multiplying by some value, such as effect’s speed bonus). Think virtual ApplyEffect(ref Affectables affectables).
For more complex effects that change the behaviour, I’ve split my character’s Update (and others) method to sections responsible for movement, firing, jumping, etc, and let them be ‘overridable’ by effects. I iterate over the effects, and when there’s one that says it has a replacement for the movement code, I call that instead of the default movement routine. Same for firing or any other action.
This way you can then write the effect classes separately, worrying much less about their mutual interference. Now you can have a generalized way to interact with the effects, such as displaying their icons, applying them (ingame or in some debug tools), networking them and so on. I hope this helps.