In the latest video by Rx team Bart De Smet: Rx Update - .NET 4.5, Async, WinRT I saw that WinRT events exposed to .NET by some really strange metadata, more preciesly - add_
/remove_
pair methods signature:
EventRegistrationToken add_MyEvent(EventHandler<MyEventArgs> handler) { … }
void remove_MyEvent(EventRegistrationToken registrationToken) { … }
It looks really great, allowing unsubscribing from event by "disposing" the registration token (Rx does the same kind of thing, returning IDisposable
instance from Subscribe()
method). So it's became possible to easily unsubscribe lamba-expressions from events, but...
So how does C# allows for working with this kind of events? In .NET it's possible to subscribe an method (static and instance) with one instance on delegate and unsubscribe with completely another delegate instance pointed to the same method. So if I using an WinRT event and just do unsubscribing of some delegate type instance in C#... where did compiler get the correct EventRegistrationToken
? How all this magic works?
-- update --
Actually EventRegistrationToken
doesn't allows to unsubscribe simply by calling some kind of Dispose()
method, that is really sadly:
public struct EventRegistrationToken
{
internal ulong Value { get; }
internal EventRegistrationToken(ulong value)
public static bool operator ==(EventRegistrationToken left, EventRegistrationToken right)
public static bool operator !=(EventRegistrationToken left, EventRegistrationToken right)
public override bool Equals(object obj)
public override int GetHashCode()
}
-- update2 --
WinRT interoperability actually uses global table of registration tokens when subscribing WinRT events with managed objets. For example, interop code for removing handlers looks like this:
internal static void RemoveEventHandler<T>(Action<EventRegistrationToken> removeMethod, T handler)
{
object target = removeMethod.Target;
var eventRegistrationTokenTable = WindowsRuntimeMarshal.ManagedEventRegistrationImpl.GetEventRegistrationTokenTable(target, removeMethod);
EventRegistrationToken obj2;
lock (eventRegistrationTokenTable)
{
List<EventRegistrationToken> list;
if (!eventRegistrationTokenTable.TryGetValue(handler, out list)) return;
if (list == null || list.Count == 0) return;
int index = list.Count - 1;
obj2 = list[index];
list.RemoveAt(index);
}
removeMethod(obj2);
}
That is really sadly.
When you add or remove a delegate to an WinRT event, like this:
It looks just like you were working with normal .Net events. But this code actually compiles to something like this (Reflector seems to have some trouble decompiling WinRT code, but I think this is what the code actually does):
This code won't actually compile, because you can't access the
add_
andremove_
methods from C#. But you can that in IL, and that's exactly what the compiler does.It looks like
WindosRuntimeMarshal
keeps all thoseEventRegistrationToken
s and uses them to unsubscribe when necessary.Would you accept "there are some amazingly clever people working on the C# language projection" as an answer?
More seriously, what you've discovered is the low level ABI (binary) implementation of the event pattern, the C# language projection knows this pattern and knows how to expose it as C# events. There are classes inside the CLR that implement this mapping.