Q:

C# Creating a basic event manager using Action, passing optional params to Invoke

I'm trying to create a simple event manager, but I'm struggling to create it the way I want.
Here is what I have so far, and it works.  However, I can not figure out how I can allow for different parameters and them being optional.
using System.Collections.Generic;
using System;

public static class Event_Manager {

    public static Dictionary<string, Action> event_list = new Dictionary<string, Action>(){

        {
            "enemy_killed", null
        },

        {
            "enemy_spawned", null
        }

    };

    public static void on(string evt, Action act){
        Action item;

        if(event_list.TryGetValue(evt, out item)){
            event_list[evt] += act;
        }
    }

    public static void off(string evt, Action act){
        Action item;

        if(event_list.TryGetValue(evt, out item)){
            event_list[evt] -= act;
        }
    }

    public static void trigger(string evt){
        Action item;

        if(event_list.TryGetValue(evt, out item)){
            item.Invoke();
        }
    }

}

Example of using it:
public void some_method(){
    // Do something when an enemy has been killed;
}

Event_Manager.on("enemy_killed", some_method);

Event_Manager.trigger("enemy_killed");

What I would like is to be able to do is pass different types of parameters as well (or some sort of object that could be an event that the methods receive).
public void player_damaged(int damage){
    // Reduce health
}

Event_Manager.on("player_hit", player_damaged);

Event_Manager.trigger("player_hit", 15);

Any help would be much appreciated.
Thanks

A:

As you surely have noticed the delegate type Action has no parameters. Keep that in mind.

One solution is to pass an object with the extra parameters. The detail with this is that you don't know the types or even the quantity of the parameters you need.
In .NET this was solved by having EventArgs, and for each new event that needs
a different combination of extra parameters a derived type is created.
That means you would use it like:
Event_Manager.trigger("player_hit", new PlayerHitEventArgs(15));

Where PlayerHitEventArgs is a class that inherits from EventArgs, and the trigger method takes an EventArgs. Similary, you would be using Action<EventArgs> (both in parameters and in the internal dictionary).
For what I get, you want to avoid the hassle.

The next option is to always pass an object, then the recieving party will have to check the type and try to cast it. Or better yet, pass dynamic.
In that case you would be using Action<dynamic>, the trigger method would take dynamic, and now you can pass anonymous types:
public void player_damaged(dynamic data){
    var damage = data.damage;
    // Reduce health
}

Event_Manager.on("player_hit", player_damaged);

Event_Manager.trigger("player_hit", new {damage = 15});

Note: also change the type of the dictionary accordingly.

If you want the code to be able to detect how many parameters does the act method have, and try to pass the parameters accordingly you will need a bit of reflection.
First you will have to relax the type from Action to simply Delegate because you will be passing things that take all amounts of parameters.
Then, in order to invoke, you first need to read what parameters does the current delegate have. To do that, you will have to get the MethodInfo of the delegate:
MethodInfo methodInfo = item.Method;

And you also need the target object of the delegate, because it may not be an static method:
object target = item.Target;

Now, we can read the parameter list frm the MethodInfo:
var paramList = method.GetParameters();

We have to build an array to invoke the method, the size we get from the list:
var args = new object[paramList.Length];

And start populating it with the values from the object. There is no need to use dynamic here.
Code for trigger:
public static void trigger(string evt, object obj){
    Delegate item;

    if(event_list.TryGetValue(evt, out item)){
        // Get MethodInfo and Target
        MethodInfo methodInfo = item.Method;
        object target = item.Target; 
        // Get the parameter list
        var paramList = methodInfo.GetParameters();
        // Get the type of the obj
        var type = obj.GetType();
        // Build the argument list
        var args = new object[paramList.Length];
        for (int index = 0; index < paramList.Length; index++)
        {
            var parameter = paramList[index];
            var name = parameter.Name;
            // Get the value from obj
            var property = type.GetProperty(name);
            var value = property.GetValue(obj, null);
            args[index] = value;
        }
        // Invoke
        methodInfo.Invoke(target, args);
    }
}

Note: no exception handling. Also remember to undate the dictionary that holds the delegates.
Example usage:
public void player_damaged(int damage){
    // Reduce health
}

Event_Manager.on("player_hit", new Action<int>(player_damaged));

Event_Manager.trigger("player_hit", new {damage = 15});

The property damage passed in trigger is mapped to the parameter damage in player_damage by name. I tested this works.
Note: Since on would be taking Delegate the compiler can't choose a delegate type for the "method group", so the cast to a delegate type is needed.

