user2136963
user2136963

Reputation: 2606

How to subscribe to an event with certain arguments c#?

I have an enumeration prior.

Each of my scripts has a property priority of prior type. (Every script has its own class)

I have a data provider, which can send events every frame.

I want a script to subscribe only to an event which has arguments with priority equal to the script's one.

For example, a script with moderate priority should receive only events with moderate parameter of event arguments

prior has too many members to create a special event argument class for each.

Unfortunately:

a)I know only how to subscribe to a certain event type.

b)I can't make a generic class for event arguments, because elements of enum are not types

How can I do it?


The project currently looks this way:

public class TDefault:MonoBehaviour,IDefault
{ 

    public enum prior
    {
        none,
        ...,
        terminal
    };
    prior priority;
    public virtual void apply()//For override by scripts
    {

    }
    void Start()
    {
        //There should be adding a method which calls apply() when event_manager 
        //sends Event with a certain priority
    }
    public TDefault ()
    {
        if(essential==null)
        essential=new TEssential();
    }

}

public class TApplyEventParam : EventArgs 
{
    public TDefault.prior priority;
    public TApplyEventParam(TDefault.prior _priority)
    {
        priority=_priority;
    }
}

    public class event_manager : TDefault
    {
        //This has fixed type
        event EventHandler<TApplyEventParam> handler=new EventHandler<TApplyEventParam>();
        void Update () 
        {
            foreach (prior p in (prior[]) Enum.GetValues(typeof(prior)))
            {
                handler(this,new TApplyEventParam(p));
            }
        }
    }

Upvotes: 0

Views: 266

Answers (1)

cgijbels
cgijbels

Reputation: 6114

The problem you're dealing with, if I understood it correctly, is that you would like to have your event subscription conditionally called depending on the event payload (the priority value inside the TApplyEventParam). That is something that you cannot do which results in you having to filter out the unwanted events inside your event handler like proposed by @Henk-Holterman

Another approach could be to skip the usage of events and maintain your own list of subscribers inside the data provider. Based on the terminology used by you in your question (not the code example) you could do something like this:

using System;
using System.Collections.Generic;

namespace Example
{
    public enum Prior
    {
        None,
        Moderate,
        Terminal
    };

    public abstract class ScriptBase
    {
        public abstract Prior Prior { get; }

        public abstract void Apply();

        public void Start(DataProvider dataProvider)
        {
            dataProvider.Subscribe(Prior, Apply);
        }

        public void Stop(DataProvider dataProvider)
        {
            dataProvider.Unsubscribe(Prior, Apply);
        }
    }

    public class ScriptHandlingModerateEvents : ScriptBase
    {
        public override Prior Prior
        {
            get { return Example.Prior.Moderate; }
        }

        public override void Apply()
        {
            Console.WriteLine("Handling moderate event by " + GetType().Name);
        }
    }

    public class ScriptHandlingTerminalEvents : ScriptBase
    {
        public override Prior Prior
        {
            get { return Example.Prior.Terminal; }
        }

        public override void Apply()
        {
            Console.WriteLine("Handling terminal event by " + GetType().Name);
        }
    }

    public class DataProvider
    {
        private readonly Dictionary<Prior, List<Action>> _subscribersByPrior;

        public DataProvider()
        {
            _subscribersByPrior = new Dictionary<Prior, List<Action>>();

            foreach (Prior prior in (Prior[])Enum.GetValues(typeof(Prior)))
            {
                _subscribersByPrior.Add(prior, new List<Action>());
            }
        }

        public void Subscribe(Prior prior, Action action)
        {
            _subscribersByPrior[prior].Add(action);
        }

        public void Unsubscribe(Prior prior, Action action)
        {
            _subscribersByPrior[prior].Remove(action);
        }

        public void DoSomethingThatTriggersPriorEvents(int someValue)
        {
            Prior prior = someValue % 2 == 0 ? Prior.Moderate : Prior.Terminal;
            foreach (var subscriber in _subscribersByPrior[prior])
            {
                subscriber();
            }
        }
    }

    public static class Program
    {
        public static void Main()
        {
            DataProvider dataProvider = new DataProvider();

            var scriptHandlingModerateEvents = new ScriptHandlingModerateEvents();
            scriptHandlingModerateEvents.Start(dataProvider);

            var scriptHandlingTerminalEvents = new ScriptHandlingTerminalEvents();
            scriptHandlingTerminalEvents.Start(dataProvider);

            for (int i = 0; i < 10; i++)
            {
                dataProvider.DoSomethingThatTriggersPriorEvents(i);
            }

            scriptHandlingTerminalEvents.Stop(dataProvider);
            scriptHandlingModerateEvents.Stop(dataProvider);

            Console.WriteLine();
        }
    }
}

this way the DataProvider is not aware of scripts, but if that is not an issue, you could maintain a list of ScriptBase instances and check the Prior property inside the DoSomethingThatTriggersPriorEvents like this:

public class DataProvider2
{
    private readonly List<ScriptBase> _scripts = new List<ScriptBase>();

    public void Subscribe(ScriptBase script)
    {
        _scripts.Add(script);
    }

    public void Unsubscribe(ScriptBase script)
    {
        _scripts.Remove(script);
    }

    public void DoSomethingThatTriggersPriorEvents(int someValue)
    {
        Prior prior = someValue % 2 == 0 ? Prior.Moderate : Prior.Terminal;
        foreach (var script in _scripts)
        {
            if (script.Prior == prior)
            {
                script.Apply();
            }
        }
    }
}

Upvotes: 1

Related Questions