2011-10-14 11 views
8

En el último vídeo por Rx equipo Bart De Smet: Rx Update - .NET 4.5, Async, WinRT vi que los eventos WinRT expuestos a .NET por algunos metadatos realmente extraño, más preciesly - add_/remove_ métodos par firma:eventos WinRT se interoperar con .NET

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

Parece realmente genial, lo que permite anular la suscripción al evento "desechando" el token de registro (Rx hace el mismo tipo de cosas, devolviendo la instancia IDisposable del método Subscribe()). Por lo tanto, es posible cancelar fácilmente la suscripción a expresiones lamba de eventos, pero ...

Entonces, ¿cómo C# permite trabajar con este tipo de eventos? En .NET es posible suscribir un método (estático y de instancia) con una instancia en el delegado y anular la suscripción con otra instancia delegada completamente apuntada al mismo método. Entonces, si utilizo un evento WinRT y simplemente cancelo la suscripción de alguna instancia de tipo de delegado en C# ... ¿de dónde obtuvo el compilador el EventRegistrationToken correcto? ¿Cómo funciona toda esta magia?

- Actualización -

En realidad EventRegistrationToken no permite darse de baja, simplemente llamando a algún tipo de Dispose() método, que es realmente 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() 
} 

- Actualización 2 -

Interoperabilidad WinRT en realidad usa global tabla de registro Tokens de señalización cuando se suscriben eventos WinRT con objetos administrados. Por ejemplo, el código de interoperabilidad para eliminar controladores se ve así:

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

Eso es realmente triste.

+3

Tenga en cuenta que todo lo que ha descrito es un detalle de la implementación y puede modificarse en cualquier momento. NUNCA debes confiar en ningún comportamiento que utilices para la ingeniería inversa. –

Respuesta

14

Al agregar o quitar un delegado a un evento WinRT, así:

this.Loaded += MainPage_Loaded; 

this.Loaded -= MainPage_Loaded; 

Se ve como que estaba trabajando con los eventos normales .Net. Pero en realidad este código se compila a algo como esto (Reflector parece tener algún código WinRT problemas descompilación, pero creo que esto es lo que realmente hace el código):

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

Este código no se compilará en realidad, porque se puede 'accede a los métodos add_ y remove_ desde C#. Pero puedes hacerlo en IL, y eso es exactamente lo que hace el compilador.

Parece que WindosRuntimeMarshal guarda todos esos EventRegistrationToken s y los utiliza para anular su suscripción cuando sea necesario.

+0

Oh no, parece que es realmente mantener la ** tabla global ** de todas las suscripciones de objetos administrados ... – ControlFlow

+0

MSDN tiene un artículo bastante bueno sobre esto en [Eventos personalizados y acceso a eventos en Windows Runtime Components] (http://msdn.microsoft.com/en-us/library/windows/apps/hh972883.aspx). @svick, no estabas tan lejos – FriendlyGuy

9

¿Aceptaría "hay algunas personas increíblemente inteligentes trabajando en la proyección del lenguaje C#" como respuesta?

Más en serio, lo que ha descubierto es la implementación ABI (binaria) de bajo nivel del patrón de eventos, la proyección de lenguaje C# conoce este patrón y sabe cómo exponerlo como eventos C#. Hay clases dentro del CLR que implementan esta asignación.

+1

Suena más como que el equipo de C# WinRT está poniendo lápiz labial en el cerdo :) –

+4

¿Por qué lápiz labial en un cerdo? Así es exactamente cómo funcionan las proyecciones de lenguaje. El tiempo de ejecución de Windows tiene un patrón de eventos (y un patrón asíncrono y un patrón de colecciones y otros patrones). Cada API que expone eventos usa el mismo patrón. El trabajo de una proyección de lenguaje es tomar API que usan ese patrón y hacer que se sientan naturales y familiares para los usuarios de ese idioma. El tiempo de ejecución de Windows no pudo simplemente adoptar el patrón de eventos C#, debido a dos razones: el tiempo de ejecución de Windows tiene que ser independiente del idioma y el tiempo de ejecución utiliza el conteo de referencias para la administración de por vida. –

+4

Desafortunadamente también es correcto. La proyección de bajo nivel para los eventos es sencilla: hay un add de evento y un método de eliminación de evento, el add toma un delegado y devuelve un token, remove toma el token. Cuando se dispara el evento, se llama al delegado. El CLR mapea este mecanismo de bajo nivel en una versión compatible con CLR. Los nombres reales de las clases utilizadas son un detalle de implementación y están sujetos a cambios de una compilación a otra. JavaScript tiene una implementación totalmente diferente del mismo mecanismo que C++. Y todas las implementaciones están sujetas a cambios. –