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

时间:2016-04-18 00:23:07

标签: c# android unity3d reflection delegates

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

现有项目有各种不同代表签名的活动, 像这样:

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

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

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

问题是:如果在不同的委托中使用不同数量和类型的参数,你如何向任何事件类型添加泛型方法(只要它返回void)?

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

2 个答案:

答案 0 :(得分:0)

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

然后,您可以创建匹配所有所需代理的不同方法。

我真的不明白为什么你只需要在运行时添加方法,因为你计划在Awake上进行,这意味着你计划知道所有需要的方法

答案 1 :(得分:0)

我能够调整this SO answer中提供的代码来解决我的问题:

    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()执行时间大量命中,所以我最终编写了一堆音频助手类。