0

So for example a List containing 5 Action<T>s A, B, C, D, and E where B calls A or C at some point within its own "Actioning".

Obviously I could do something like this (please do not get distracted by the uselessness of this example):

    public static List<Action<string>> ActionList = new List<Action<string>>
    {
        inputString => //print reverse of string
        {
            Console.WriteLine(inputString.Reverse());
        },
        inputString => //print reverse of string if "Cat" otherwise print the string twice
        {
            if (inputString.Equals("Cat"))
            {
                ActionList[0](inputString);
            }
            else
            {
                ActionList[2](inputString);
            }
        },
        inputString => //print the string twice
        {
            Console.WriteLine(inputString);
            Console.WriteLine(inputString);
        },
        inputString => //print the string in uppercase
        {
            Console.WriteLine(inputString.ToUpper());
        },
        inputString => //print the string in lowercase
        {
            Console.WriteLine(inputString.ToLower());
        },
    };

The real thing would contain hundreds of Actions, each far more complex, many calling 10s of other Actions in different ways inside switch statements, etc.

I preferably want to have each Action wrapped into their own static class so I can keep all the code relevant to making that Action work that I've written in that class too, perhaps even some extra properties too. But of course that's still a pain because I still have to hardcode the List and add elements every time I add a new Action and even though I can make a common interface for each wrapper to implement, I cannot make it static (I understand why interfaces can't be static) despite only ever needing "static behaviour" which is an immediate red flag.

Anyone know a good way to do this?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Shefeto
  • 178
  • 1
  • 10
  • 1
    Why don't you use the observer pattern for this one? – k0pernikus May 14 '18 at 11:23
  • I'm simply interesting, why do you need such huge construction? – Yurii N. May 14 '18 at 11:24
  • Is that list being "compile time" really a requirement? If you can build it (once) at runtime - that would be easier. – Evk May 14 '18 at 11:26
  • @Evk, if you can think of a run time solution I would use it if no compile time one is found yes. – Shefeto May 14 '18 at 11:37
  • And how are you going to use this list? I mean those actions doesn't have names or something, how do you know which one to call in given context? Or you are going to always loop through all actions and execute them all for given input? – Evk May 14 '18 at 11:39
  • @YuriyN. No problem thanks for asking, I plan to use it for AI behaviour in a large simulation containing AIs, the list is required to cycle through as each Action is evaluated by each AI every so often and "scored", then they perform the Action they scored most highly. I have no clue whether it will be at all practical but I'm very motivated to experiment with it! I may have to use List> in order to get the "scores" but that shouldn't change the answer to this question. – Shefeto May 14 '18 at 11:41
  • @Evk see my new comment to YuriyN – Shefeto May 14 '18 at 11:42
  • And so, order of actions is important too? – Evk May 14 '18 at 11:43
  • Have you considered dynamic classes? It could allow you to name these compile-time Actions as though they were methods while keeping them in a collection. –  May 14 '18 at 11:45
  • @Evk No, I just used List out of habit, my mistake. They are all evaluated and the highest scored Action is picked, the order they are checked doesn't matter. – Shefeto May 14 '18 at 11:46
  • @Halex do you mean like this? https://stackoverflow.com/questions/3862226/how-to-dynamically-create-a-class-in-c Or did you mean something else? – Shefeto May 14 '18 at 11:52
  • I think a reasonable thing to do is use separate class for each action, all inheriting from some base abstract class (`absract class BaseAction { abstract string Name {get;} abstact void Execute(T input); }`). Name is good because you are going to reference one actions from another, and doing that with index is just not maintainable. Then at runtime you can build your list (actually a `Dictionary`) by exploring all types inheriting from base action in assembly. Building list is somewhat slow (compared to compile time list), but that's just one time at startup. – Evk May 14 '18 at 11:57
  • @Evk I already explain in my question why I'm against having each Action in a unique wrapper with a common Base class/interface: they are statically acting, I shouldn't need an instance to call the Action, but this way I would, which as I said is a huge red flag of doing something wrong. – Shefeto May 14 '18 at 12:00
  • @k0pernikus I'm not sure what you mean, I am comfortable with the observer pattern https://learn.microsoft.com/en-us/dotnet/standard/events/observer-design-pattern but cannot figure how it applies here, if you could elaborate I'd appreciate it, thanks. – Shefeto May 14 '18 at 12:02
  • No, I mean something that would implement the `DynamicObject` class, some kind of improved dictionary really. You could call a method directly, give it params when needed (Possibility for dependency injection of the _sub-actions_) and keep everything fairly clean and extensible. –  May 14 '18 at 12:04
  • @Halex I feel like this just passes the baton doesn't it? All it really allows is that I can name them and call them like methods, but it's not very maintainable or readable or compile time friendly. – Shefeto May 14 '18 at 12:15
  • I don't see it as a red flag to be honest. There will be one instance of each action in your list\dictionary - what's a problem in that? If that helps, you can consider them singletons. Alternatively - you can have one big class with all those actions as static methods, named as usual. Then on startup, build a list of those methods via reflection and fill your list. – Evk May 14 '18 at 13:23

0 Answers0