В последнем видео от команды Rx Барт Де Смет: Обновление Rx - .NET 4.5, Async, WinRT Я видел, что события WinRT подвергаются .NET некоторыми действительно странными метаданными, более точно - add_
/ remove_
сигнатура парных методов:
EventRegistrationToken add_MyEvent(EventHandler<MyEventArgs> handler) { … }
void remove_MyEvent(EventRegistrationToken registrationToken) { … }
Выглядит действительно здорово, позволяя отказаться от подписки на событие, «утилизировав» регистрационный токен (Rx делает то же самое, возвращаяIDisposable
экземпляр из Subscribe()
метода).Таким образом, стало возможным легко отписывать лямба-выражения от событий, но ...
Так как же 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()
}
- update2 -
Взаимодействие 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);
}
Это действительно печально.