繁体   English   中英

如何在运行时(C#+ Unity)将方法添加到ANY事件的调用列表中

[英]How do I add a method to ANY event's invocation list at runtime (C# + Unity)

我正在尝试为Unity(5.x)创建一个基于事件的声音触发系统,该系统建立在一个项目上,在该项目中应尽可能少地修改现有代码。

现有项目具有各种事件,这些事件具有不同的委托人签名,如下所示:

public delegate void CustomDelegateType1();
public event CustomDelegateType1 OnCustomEvent1;

public delegate void CustomDelegateType2(CustomClass param);
public event CustomDelegateType2 OnCustomEvent2;

我想通过在运行时向调用列表中添加一个方法来使用这些事件来触发声音,就像对待其他任何订户一样对待新方法。 基本上,首先使用反射获取当前GameObject上的事件列表,在我的Component的编辑器下拉列表中显示这些事件,然后在Awake()上将方法绑定到该事件

问题是:在不同的委托中使用不同数量和类型的参数的情况下,如何将通用方法添加到ANY事件类型中(只要它返回void)?

[编辑:这也需要在移动设备上运行,因此Reflection.Emit使用不可行]

似乎您只想注册某些事件(因为要在UI中进行选择。为什么不创建一个类来管理音频并注册所需的所有事件?)。

然后,您可以创建与所有所需委托匹配的不同方法。

我真的不明白为什么您只需要在运行时添加方法,因为您打算在Awake上进行操作,这意味着无论如何您都打算了解所有需要的方法

我能够修改此SO答案中提供的代码来解决我的问题:

    public static Delegate AddHandler(this object obj, string eventName, Action action)
{
    // Filter list by event name
    EventInfo ev = obj.GetType().GetEvents().Where(x => x.Name == eventName).FirstOrDefault();
    if (ev == null)
    {
        Debug.LogWarning(eventName + " not found on " + obj.ToString());
        return null;
    }

    // Simple case - the signature matches so just add the new handler to the list
    if (ev.EventHandlerType == typeof(Action))
    {
        ev.AddEventHandler(obj, action);

        return action;
    }

    Delegate del = CreateDelegate(ev, action);
    ev.AddEventHandler(obj, del);

    return del;
}

public static void RemoveHandler(this object obj, string eventName, Action action)
{
    // Filter list by event name
    var ev = obj.GetType().GetEvents().Where(x => x.Name == eventName).FirstOrDefault();
    if (ev == null)
    {
        Debug.LogWarning(eventName + " not found on " + obj.ToString());
        return;
    }

    // Simple case - the signature matches so just add the new handler to the list
    if (ev.EventHandlerType == typeof(Action))
    {
        ev.RemoveEventHandler(obj, action);
    }
    else
    {
        Delegate del = CreateDelegate(ev, action);
        ev.RemoveEventHandler(obj, del);
    }
}

private static Delegate CreateDelegate(EventInfo ev, Action action)
{
    // Retrieve the parameter types of the event handler
    var parameters = ev.EventHandlerType.GetMethod("Invoke").GetParameters();

    ParameterExpression[] parameters2 = Array.ConvertAll(parameters, x => Expression.Parameter(x.ParameterType, x.Name));
    MethodCallExpression call;

    // We are "opening" the delegate and directly using the Target and the Method.
    if (action.Target == null) // Event is static
        call = Expression.Call(action.Method);
    else // Event is instanced
        call = Expression.Call(Expression.Constant(action.Target), action.Method);

    var exp = Expression.Lambda(ev.EventHandlerType, call, parameters2);

    return exp.Compile();
}

不幸的是,我无法将这种方法优化为足够有效地用于生产用途,因为它会极大地影响init()的执行时间,因此我最终编写了许多音频帮助程序类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM