2012-03-23 9 views
8

C# nos permite to create custom event accessors.¿El campo de respaldo de un evento generado por compilador siempre garantiza usar el mismo nombre que el evento?

Action _custom; 
public event Action Custom 
{ 
    add { _custom = (Action)Delegate.Combine(_custom, value); } 
    remove { _custom = (Action)Delegate.Remove(_custom, value); } 
} 

Si no se especifica ellos, the compiler creates them for you. C# spec idioma:

Al compilar un evento de campo como, el compilador crea automáticamente de almacenamiento para guardar el delegado, y crea descriptores de acceso para el caso de que añadir o eliminar controladores de eventos al campo delegado.

El código fuente descompilada usando dotPeek para un simple public event Action Public; se ve de la siguiente manera:

private Action Public; 

    public event Action Public 
    { 
    add 
    { 
     Action action = this.Public; 
     Action comparand; 
     do 
     { 
     comparand = action; 
     action = Interlocked.CompareExchange<Action>(
        ref this.Public, comparand + value, comparand); 
     } 
     while (action != comparand); 
    } 
    remove 
    { 
     Action action = this.Public; 
     Action comparand; 
     do 
     { 
     comparand = action; 
     action = Interlocked.CompareExchange<Action>(
        ref this.Public, comparand - value, comparand); 
     } 
     while (action != comparand); 
    } 
    } 

Es de destacar que el campo y el evento utilizan el mismo nombre. Esto ha llevado al some people a concluir que puede encontrar información sobre el campo de respaldo durante la reflexión buscando el campo en la clase con el mismo nombre que el evento. He implementado de la siguiente manera:

public static FieldInfo GetFieldInfo(this EventInfo eventInfo) 
{ 
    Contract.Requires(eventInfo != null); 

    return eventInfo.DeclaringType.GetField(
     eventInfo.Name, 
     BindingFlags.DeclaredOnly | BindingFlags.Instance | 
      BindingFlags.Public | BindingFlags.NonPublic); 
} 

Esto funciona, pero plantea la pregunta: ¿es el campo respaldo de un evento generado por el compilador siempre garantizado para utilizar el mismo nombre que el evento?

No es posible crear acceso a eventos personalizados que accedan a un delegado con el mismo nombre que utiliza Visual Studio. Esto da como resultado el mensaje: "El miembro con el mismo nombre ya está declarado". Me pregunto si podría concluir que cualquier evento para el que no esté disponible un delegado de respaldo con el mismo nombre sea un evento con acceso directo personalizado.

Respuesta

9

¿El campo de respaldo de un evento generado por compilador siempre garantiza usar el mismo nombre que el evento?

Jon y Marc son completamente correctos para responder "No".

Este es un detalle de implementación no documentado del compilador, anotado explícitamente por la especificación como tal, y sujeto a cambios en cualquier momento.

En la práctica, es poco probable que esto cambie. Usamos el hecho de que el campo y el evento tienen el mismo nombre que la forma más simple de asociarlos lógicamente entre sí en el compilador.

No es posible crear acceso a eventos personalizados que accedan a un delegado con el mismo nombre utilizando Visual Studio. Esto da como resultado el mensaje: "El miembro con el mismo nombre ya está declarado".

Correcto.

Me pregunto si puede concluir que cualquier evento para el que no esté disponible un delegado de respaldo con el mismo nombre es un evento con acceso directo personalizado.

No me sentiré cómodo al llegar a esa conclusión. Es posible que pueda llegar a esa conclusión si sabía que el ensamblado en cuestión se había emitido desde el compilador de C#. Pero no somos el único juego en la ciudad cuando se trata de emitir ensamblajes. Puedes hacer algunas cosas bastante extrañas con ILDASM.

¿Puedo preguntarte por qué quieres saber esto? Estoy de acuerdo con Marc; si está accediendo a un campo a través de Reflection, probablemente lo esté haciendo mal. Debería poder acceder al campo dentro de la clase sin problemas (porque es solo un campo privado) y desde fuera de la clase, no tiene que ver los detalles privados de implementación de otra clase. Es particularmente atroz usar el reflejo para hacer un recorrido final alrededor de la seguridad de la rosca impuesta por los accesorios. Esos accesadores están ahí para su protección; no corras a su alrededor.

+1

_ "¿Puedo preguntarte por qué quieres saber esto?" _ 1. ¡Porque es divertido e instructivo! :) 2. Estaba escribiendo un aspecto usando [PostSharp] (http://www.sharpcrafters.com/) que en cierto punto tenía que saber sobre el delegado del campo de respaldo. Pensé que podría ser un problema y pude utilizar otro enfoque. –

+0

3. Como no he visto ningún problema con, por ejemplo, [esta respuesta] (http://stackoverflow.com/questions/3783267/how-to-get-a-delegate-object-from-an-eventinfo/3783491#3783491) Pensé que podría valer la pena preguntarlo específicamente. Estoy empezando a ver una correlación entre mis preguntas y el trío mágico que las responde. ¡Muchas gracias! :) También noto que realmente debería comenzar a leer esa especificación de C#. ¡Cosas interesantes! –

+0

Y una divulgación 100% completa, estoy escribiendo un aspecto que elimina la necesidad de que escriba _ "delegar {}" _ al crear eventos, [de ahí mi pregunta anterior] (http://stackoverflow.com/q/9823457/590790). :) Me encantan este tipo de cosas. Incluso si pudiera ser excesivo, es una excelente forma de aprender. :) –

6

n - de la especificación C# 4 (sección 10.8.1):

Dentro de la clase X, las referencias a Ev se compilan para hacer referencia al campo oculto _ Ev lugar. El nombre " _Ev" es arbitrario; el campo oculto podría tener cualquier nombre o no tener ningún nombre.

Por lo tanto, aunque se garantiza la compatibilidad con el código fuente, no hay garantía sobre el nombre del campo generado. (En la práctica, no esperaría que esto cambie pronto en el compilador de MS, pero no está garantizado, por lo que no debe hacer suposiciones).

+0

Bien, gracias! Entonces, usar 'GetField (eventName)' para encontrar el delegado de respaldo solo funcionaría para los ensamblados compilados con el compilador 'correcto'. Puede ser interesante saber cuántos compiladores usan un nombre diferente. –

+1

@StevenJeuris: No solo eso: se supone que el evento se está implementando con un evento tipo campo en primer lugar. Como dice Marc, si necesitas ir detrás de la implementación, lo estás haciendo mal :( –

5

La implementación de un evento es un detalle de implementación del compilador, y difiere entre compiladores (MS C# 4 tiene una implementación diferente a MS C# < 4, e incluso las especificaciones MS y ECMA no están de acuerdo).

Personalmente, diría: si necesita acceder al campo de respaldo a través de la reflexión, es probable que no esté utilizando los eventos correctamente.

+0

_ "probablemente no estés usando eventos correctamente" _ ... el mundo de los aspectos es un lugar maravilloso. :) Pero tienes razón, ya logré escribir mi aspecto de manera diferente, eliminando la necesidad de obtener el campo de respaldo. Me preguntaba si debería mantener 'GetFieldInfo()' en mi biblioteca, ya que se sentía peligroso. Se ha ido ahora. ; p –

Cuestiones relacionadas