C#使用Action创建基本事件管理器,将可选参数传递给Invoke

时间:2016-02-17 16:24:30

标签: c# events

我正在尝试创建一个简单的事件管理器,但我正在努力按照我想要的方式创建它。

这是我到目前为止所做的,并且它有效。但是,我无法弄清楚如何允许不同的参数并且它们是可选的。

public void some_method(){
    // Do something when an enemy has been killed;
}

Event_Manager.on("enemy_killed", some_method);

Event_Manager.trigger("enemy_killed");

使用它的例子:

public void player_damaged(int damage){
    // Reduce health
}

Event_Manager.on("player_hit", player_damaged);

Event_Manager.trigger("player_hit", 15);

我希望能够做的是传递不同类型的参数(或某种对象,可能是方法接收的事件)。

(\d+-(?:WARN|PREVENT).*?)(?=,\d+-(?:WARN|PREVENT)|,$)

非常感谢任何帮助。

由于

2 个答案:

答案 0 :(得分:1)

您肯定注意到委托类型Action没有参数。记住这一点。

一种解决方案是使用额外参数传递对象。这方面的细节是您不知道所需参数的类型甚至数量。

在.NET中,这可以通过EventArgs和每个需要的新事件来解决 创建派生类型的额外参数的不同组合。

这意味着您可以像以下一样使用它:

Event_Manager.trigger("player_hit", new PlayerHitEventArgs(15));

PlayerHitEventArgs是继承自EventArgs的类,而trigger方法需要EventArgs。同样,您将使用Action<EventArgs>(在参数和内部字典中)。

我得到的,你想避免麻烦。

下一个选项是始终传递object,然后接收方必须检查类型并尝试投射它。或者更好的是,通过dynamic

在这种情况下,您将使用Action<dynamic>,触发器方法将使用dynamic,现在您可以传递匿名类型:

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});

注意:也相应地更改字典的类型。

如果您希望代码能够检测act方法具有多少参数,并尝试相应地传递参数,则需要进行一些反思。

首先,您必须放宽从ActionDelegate的类型,因为您将传递带有所有参数的内容。

然后,为了调用,首先需要读取当前委托具有的参数。为此,您必须获得代理人的MethodInfo

MethodInfo methodInfo = item.Method;

并且您还需要委托的目标对象,因为它可能不是静态方法:

object target = item.Target;

现在,我们可以阅读参数列表frm MethodInfo

var paramList = method.GetParameters();

我们必须构建一个数组来调用方法,我们从列表中获取的大小:

var args = new object[paramList.Length];

然后开始使用对象中的值填充它。这里没有必要使用动态。

触发代码:

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);
    }
}

注意:没有异常处理。还要记住要打开包含代表的字典。

使用示例:

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});

damage中传递的属性trigger按名称映射到damage中的参数player_damage。我测试了这个作品。

注意:由于on将采用Delegate,编译器无法为&#34;方法组&#34;选择委托类型,因此需要转换为委托类型。

答案 1 :(得分:1)

然后您需要将字典更改为

Dictionary<string, Action<object>> 

和触发方法就像

   public static void trigger(string evt, object parameter){
        Action<object> item;

        if(event_list.TryGetValue(evt, out item)){
           if (item != null) {
            item.Invoke(parameter);
           }
        }
    }

但在这种情况下,你会失去所有强类型方法的美感,你需要从对象中取消装箱。