Search code examples
c#reflectionunity-game-enginedelegatesmono

ArgumentException : method arguments are incompatible


I am trying to make an event system for special effects

I have this function ins a static class to allow me to link any basic event handler to any basic event, no matter the classes of each objects, as long as they have the needed functions

here is the function

(in a public static class with multiple really useful generic functions

    public static bool eventLinker<T,Y>(T listener, Y speaker, string eventName, string eventHandler)
{
    EventInfo eInfo = typeof(Y).GetEvent(eventName);
    MethodInfo evHandler = typeof(T).GetMethod(eventHandler, new Type[2] {typeof(object), typeof(EventArgs)});
    Type handlerType = eInfo.EventHandlerType;

    if(eInfo != null && evHandler != null && handlerType != null)
    {

        Delegate d = Delegate.CreateDelegate(handlerType, speaker, evHandler);
        eInfo.AddEventHandler(listener,d);

        return true;
    }
    return false;
}

this should work wich any classes who have public event EventHandler SomethingHappened;

public void nameOfTheFunctionThatTriggerTheEvent()
{
    EventHandler handler = SomethingHappened;
    if(handler != null)
    {
        handler(this, EventArgs.Empty);
    }

}

with any class who have

public void HandleEvent(object sender, EventArgs e)
{
    Debug.Log("Something happened to " + sender);
}

I always get this error at Delegate.CreateDelegate

ArgumentException: method arguments are incompatible System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Delegate.cs:263) System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Delegate.cs:276) FunctionExt.eventLinker[Effect,Test] (.Effect listener, .Test speaker, System.String eventName, System.String eventHandler) (at Assets/_C#/FunctionExt.cs:41) GameUpdate.Awake () (at Assets/_C#/GameUpdate.cs:84)

I could not find the solution anywhere.


Solution

  • First of all AddEventHandler requires target as a first parameter and you are passing listener that is not right.

    Next, when you create delegate you should use another method signature CreateDelegate because first parameter will be passed from handler invocation (see your code handler(this, EventArgs.Empty);).

    The whole code:

    public static bool eventLinker<T, Y>(T listener, Y speaker, string eventName, string eventHandler)
    {
        EventInfo eInfo = typeof(Y).GetEvent(eventName);
        MethodInfo evHandler = typeof(T).GetMethod(eventHandler, new Type[2] { typeof(object), typeof(EventArgs) });
        Type handlerType = eInfo.EventHandlerType;
    
        if (eInfo != null && evHandler != null && handlerType != null)
        {
            Delegate d = Delegate.CreateDelegate(handlerType, listener, eventHandler);
            eInfo.AddEventHandler(speaker, d);
    
            return true;
        }
        return false;
    }
    

    Or you can use alternative more generic and safety code with check of event handler existing on create delegate:

    public static bool eventLinker<T, Y>(T listener, Y speaker, string eventName, string eventHandler)
    {
        EventInfo eInfo = typeof(Y).GetEvent(eventName);
        Type handlerType = eInfo.EventHandlerType;
    
        if (eInfo != null && handlerType != null)
        {
            Delegate d = Delegate.CreateDelegate(handlerType, listener, eventHandler, false, false);
            if (d != null)
            {
                eInfo.AddEventHandler(speaker, d);
                return true;
            }
        }
        return false;
    }
    

    Last example have no hard-coded handler parameters typeof(object), typeof(EventArgs) and then can handle scenarios with custom event types.