2011-10-14 12 views
8

Nell'ultimo video Rx squadra Bart De Smet: Rx Update - .NET 4.5, Async, WinRT ho visto che gli eventi WinRT esposti a .NET da parte di alcuni davvero strano metadati, più preciesly - add_/remove_ metodi paio firma:come gli eventi WinRT sono interoperare con .NET

EventRegistrationToken add_MyEvent(EventHandler<MyEventArgs> handler) { … } 
void remove_MyEvent(EventRegistrationToken registrationToken) { … } 

Sembra davvero eccezionale, consentendo l'annullamento dell'iscrizione dall'evento "disponendo" il token di registrazione (Rx fa lo stesso tipo di cosa, restituendo l'istanza IDisposable dal metodo Subscribe()). Quindi è diventato possibile cancellare facilmente le espressioni di lamba dagli eventi, ma ...

Quindi, come C# consente di lavorare con questo tipo di eventi? In .NET è possibile sottoscrivere un metodo (statico e istanza) con un'istanza su delegato e annullare l'iscrizione con completamente un'altra istanza di delegato che punta allo stesso metodo. Quindi, se utilizzo un evento WinRT e semplicemente annullo l'iscrizione di qualche istanza di tipo delegato in C# ... dove il compilatore ha ottenuto il corretto EventRegistrationToken? Come funziona tutta questa magia?

- aggiornamento -

realtà EventRegistrationToken non permette di annullare l'iscrizione è sufficiente chiamare un qualche tipo di Dispose() metodo, che è veramente triste:

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 -

L'interoperabilità WinRT utilizza attualmente la tabella di registrazione globale token di gestione quando si sottoscrivono eventi WinRT con oggetti gestiti. Ad esempio, il codice di interoperabilità per la rimozione di gestori di simile a questa:

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); 
} 

che è davvero triste.

+3

Nota che tutto ciò che hai descritto è un dettaglio di implementazione e può essere modificato in qualsiasi momento. Non dovresti MAI fare affidamento su alcun comportamento da te decodificato. –

risposta

14

Quando si aggiunge o si rimuove un delegato a un evento WinRT, come questo:

this.Loaded += MainPage_Loaded; 

this.Loaded -= MainPage_Loaded; 

Sembra proprio come si stesse lavorando con i normali eventi .Net. Ma questo codice compila in realtà a qualcosa di simile (Reflector sembra avere qualche codice WinRT difficoltà decompilazione, ma penso che questo è ciò che fa effettivamente il codice):

WindowsRuntimeMarshal.AddEventHandler<RoutedEventHandler>(
    new Func<RoutedEventHandler, EventRegistrationToken>(this.add_Loaded), 
    new Action<EventRegistrationToken>(remove_Loaded), 
    new RoutedEventHandler(this.MainPage_Loaded)); 

WindowsRuntimeMarshal.RemoveEventHandler<RoutedEventHandler>(
    new Action<EventRegistrationToken>(this.remove_Loaded), 
    new RoutedEventHandler(this.MainPage_Loaded)); 

Questo codice non sarà effettivamente compilare, perché si può accedere ai metodi add_ e remove_ da C#. Ma puoi farlo in IL, ed è esattamente ciò che fa il compilatore.

Sembra che WindosRuntimeMarshal mantiene tutti quelli EventRegistrationToken s e li utilizza per annullare l'iscrizione quando necessario.

+0

Oh no, sembra davvero mantenere la ** tabella globale ** di tutti gli abbonati dagli oggetti gestiti ... – ControlFlow

+0

MSDN ha un buon articolo su questo in [Eventi personalizzati e accessori di eventi in Componenti di Windows Runtime] (http://msdn.microsoft.com/en-us/library/windows/apps/hh972883.aspx). @vick, non eri così lontano – FriendlyGuy

9

Accettate "ci sono persone straordinariamente intelligenti che lavorano alla proiezione in linguaggio C#" come risposta?

Più seriamente, ciò che hai scoperto è l'implementazione ABI (binaria) di basso livello del pattern di eventi, la proiezione in linguaggio C# conosce questo modello e sa come esporlo come eventi C#. Ci sono classi all'interno del CLR che implementano questa mappatura.

+1

Suoni più come il team C# WinRT sta mettendo il rossetto sul maiale :) –

+4

Perché il rossetto su un maiale? Questo è esattamente il modo in cui funzionano le proiezioni linguistiche. Il runtime di Windows ha un pattern di evento (e un pattern async e un pattern di collezioni e altri pattern). Ogni API che espone eventi utilizza lo stesso modello. Il lavoro di una proiezione linguistica consiste nel prendere API che utilizzano quel modello e farle sentire naturali e familiari agli utenti di quella lingua. Il runtime di Windows non può semplicemente adottare il modello di evento C#, per due motivi: il runtime di Windows deve essere indipendente dalla lingua e il runtime utilizza il conteggio dei riferimenti per la gestione a vita. –

+4

Sfortunatamente è anche corretto. La proiezione di basso livello per gli eventi è semplice: c'è un evento add e un metodo di rimozione degli eventi, l'add prende un delegato e restituisce un token, la rimozione prende il token. Quando viene attivato l'evento, viene chiamato il delegato. Il CLR associa questo meccanismo di basso livello in una versione compatibile con CLR. I nomi effettivi delle classi utilizzate sono dettagli di implementazione e sono soggetti a modifiche da una build a un'altra. JavaScript ha un'implementazione completamente diversa dello stesso meccanismo di C++. E tutte le implementazioni sono soggette a modifiche. –