在 Rx 团队Bart De Smet 的最新视频中:Rx Update - .NET 4.5, Async, WinRT我看到 WinRT 事件通过一些非常奇怪的元数据暴露给 .NET,更准确地说 - add_
/remove_
对方法签名:
EventRegistrationToken add_MyEvent(EventHandler<MyEventArgs> handler) { … }
void remove_MyEvent(EventRegistrationToken registrationToken) { … }
它看起来真的很棒,允许通过“处理”注册令牌来取消订阅事件(Rx 做同样的事情,IDisposable
从方法返回实例Subscribe()
)。因此,可以轻松地从事件中取消订阅lamba-expressions,但是......
那么 C# 如何允许处理这种事件呢?在 .NET 中,可以使用委托上的一个实例订阅方法(静态和实例),并完全取消订阅指向同一方法的另一个委托实例。因此,如果我使用 WinRT 事件并且只是取消订阅 C# 中的某些委托类型实例......编译器在哪里得到正确的EventRegistrationToken
?所有这些魔法是如何运作的?
- 更新 -
实际上EventRegistrationToken
不允许简单地通过调用某种Dispose()
方法来取消订阅,这真的很可悲:
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()
}
-- 更新2 --
在使用托管对象订阅 WinRT 事件时,WinRT 互操作性实际上使用了全局注册令牌表。例如,用于删除处理程序的互操作代码如下所示:
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);
}
这真的很可悲。