DanH
DanH

Reputation: 3802

Observable.FromEvent & CreateDelegate param mapping

I was looking at the implemention of

Observable.FromEvent<TEventHandler, TEventHandlerArgs>(add, remove)

and I'm struggling to grasp how it works. Lets says that TEventHandler is the standard:

public delegate void EventHandler(object sender, EventArgs e);

then the code that is puzzling me is:

TEventHandler d = (TEventHandler) Delegate.CreateDelegate(
    typeof (TEventHandler),
    (object) new Action<EventArgs>(observer.OnNext),
    typeof (Action<EventArgs>).GetMethod("Invoke"));

(n.b I've specialised this generic code to this specific example instance.)

How is it that CreateDelegate is creating a delegate of signature (obj, args) that is bound to an invoke method of signature (args) on the action? Where is obj going?

It feels a bit like it might be around having an open delegate on action and we are coercing the 'this' to be 'firstArguemnt' from CreateDelegate and allowing the args to fall through. If so feels kinda dirty?

Upvotes: 12

Views: 1323

Answers (1)

Orion Edwards
Orion Edwards

Reputation: 123662

Let's break it down:

Firstly, decompiling Rx v2.0.3 doesn't seem to have a Observable.FromEvent<TEventHandler, TEventHandlerArgs>(add, remove) method, and neither does Rx v 1.1 which I happened to have lying around. I'm going to assume you mean the nearest match I can find, which is this:

public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler)

Looking at the decompiled source for Rx 1.1 (the 2.0 source has gone all architecture astronaut on us and is full of indirection which makes it much harder to follow) The actual code snippet using reflector to decompile is this:

Action<TEventArgs> o = new Action<TEventArgs>(observer.OnNext);
    TDelegate d = CreateDelegate<TDelegate>(o,
        typeof(Action<TEventArgs>).GetMethod("Invoke"));

    addHandler(d);

So, the question:

How is it that CreateDelegate is creating a delegate of signature (obj, args) that is bound to an invoke method of signature (args) on the action? Where is obj going?

I'm not sure if I've understood quite correctly, but it seems like the question specifically is something like How does CreateDelegate<TDelegate>(o, typeof(Action<TEventArgs>).GetMethod("Invoke") produce a method with only the args parameter - what happens to the o object?

What's happening is that yes, the o object is being passed as object firstArgument to the internal .NET framework method.

public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)

This method "binds" the firstArgument as basically being the this pointer for the returned method. Internally it will store a reference to the firstArgument somewhere inside the delegate object. We can't see inside that as it's an internal .NET implementation detail and so it can do all sorts of odd things and break rules where it pleases.

It feels a bit like it might be around having an open delegate on action and we are coercing the 'this' to be 'firstArguemnt' from CreateDelegate and allowing the args to fall through. If so feels kinda dirty?

Yes, that's pretty much exactly what's happening. This is what that CreateDelegate function is designed to do.
Except it gets even dirtier than that. CreateDelegate simply returns an object of type Delegate - we have no type safety on the method args, etc - and then the code casts it into a TDelegate - this works because delegate is special and you can cast it to any function type that has the same "shape". As above, it's an internal .NET implementation detail and so it can do all sorts of weird things :-)

Upvotes: 1

Related Questions