3

What I effectively want to do is something like this (I realise this is not valid code):

// Attach the event.
try
{
    EventInfo e = mappings[name];
    (e.EventHandlerType) handler = (sender, raw) =>
    {
        AutoWrapEventArgs args = raw as AutoWrapEventArgs;
        func.Call(this, args.GetParameters());
    };

    e.AddEventHandler(this, handler);
}
...

Now I know that the e.EventHandlerType will always derive from EventHandler<AutoWrapEventArgs>. However, I can't just do:

    EventHandler<AutoWrapEventArgs> handler = (sender, raw) =>
    {
        AutoWrapEventArgs args = raw as AutoWrapEventArgs;
        func.Call(this, args.GetParameters());
    };

    e.AddEventHandler(this, handler);

As .NET complains that there is no conversion applicable from EventHandler<AutoWrapEventArgs> to EventHandler<DataEventArgs> when AddEventHandler is called. This is the exact message:

Object of type 'System.EventHandler`1[IronJS.AutoWrapObject+AutoWrapEventArgs]'
cannot be converted to type
'System.EventHandler`1[Node.net.Modules.Streams.NodeStream+DataEventArgs]'.

I have also tried using Invoke to dynamically use the constructor of e.EventHandlerType, but there's no way to pass the delegate definition to Invoke()'s parameter list (because there is no conversion from delegate to object).

Is there a way I can use reflection to get around this problem?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
June Rhodes
  • 3,047
  • 4
  • 23
  • 29
  • Are you using .NET 4.0? If so you could try replacing the EventHandler with the dynamic keyword. See if that helps you... – Brian Dishaw Jun 04 '11 at 01:25
  • Nope, it's .NET 3.5. I'll update the original question with that. – June Rhodes Jun 04 '11 at 01:29
  • Hmm.. it's quite interesting that typeof(EventHandler<>).MakeGenericType(e.EventHandler.GetGenericArguments()[0]).GetConstructors()[0] returns "{Void .ctor(System.Object, IntPtr)}". I'm not particularly sure how a delegate matches up with that parameter list, but it's almost surely relevant to the solution. – June Rhodes Jun 04 '11 at 02:01

2 Answers2

2

Bingo! The trick is to get a reference to the constructor for the delegate type and then invoke it using the following parameters:

  • The target object of the delegate (backend.Target)
  • The delegate's pointer (backend.Method.MethodHandle.GetFunctionPointer())

The actual code that does this looks like (t in this case is the generic argument provided to EventHandler<> during inheritance):

Type t = e.EventHandler.GetGenericArguments()[0];
Delegate handler = (Delegate)
    typeof(EventHandler<>)
    .MakeGenericType(t)
    .GetConstructors()[0]
    .Invoke(new object[]
        {
            backend.Target,
            backend.Method.MethodHandle.GetFunctionPointer()
        });

You can then use the delegate for the event adding like so:

e.AddEventHandler(this, handler);
June Rhodes
  • 3,047
  • 4
  • 23
  • 29
  • This is exactly what I needed. In my case I'm converting `EventHandler` to non-generic delegate types with the same signature. For example, this trick will allow you to assign to the type `CancelEventHandler` from an `EventHandler` (or even an `Action`, for that matter). I've tried various approaches involving `Delegate.CreateDelegate`, but this is the only one that has worked. – nmclean Aug 23 '13 at 19:53
0

You can use Delegate.CreateDelegate to accomplish your goal like this:

public void RegisterHandler(string name)
{
    EventInfo e = mappings[name];
    EventHandler<AutoWrapEventArgs> handler = (s, raw) =>
        {
            func.Call(this, raw.GetParameters());
        };
    e.AddEventHandler(this, Delegate.CreateDelegate(e.EventHandlerType, null, handler.Method));
}
Rick Sladkey
  • 33,988
  • 6
  • 71
  • 95