c# - tutorial - ¿Por qué alguien debe estar suscrito para que ocurra un evento?
subscribe to the event c# (9)
Bueno, la forma canónica es:
void OnMadeSound()
{
if (MadeSound != null)
{
MadeSound(this, new EventArgs());
}
}
public void Fall() { OnMadeSound(); }
que es muy ligeramente más rápido que llamar a un delegado vacío, por lo que la velocidad se impuso a la conveniencia de la programación.
Algún texto antes del código para que el resumen de la pregunta no se altere.
class Tree
{
public event EventHandler MadeSound;
public void Fall() { MadeSound(this, new EventArgs()); }
static void Main(string[] args)
{
Tree oaky = new Tree();
oaky.Fall();
}
}
No he usado mucho los eventos en C #, pero el hecho de que esto causaría un NullRefEx parece extraño. La referencia de EventHandler se considera nula porque actualmente no tiene suscriptores, pero eso no significa que el evento no se haya producido, ¿verdad?
EventHandlers se diferencian de los delegados estándar por la palabra clave del evento . ¿Por qué los diseñadores de idiomas no los prepararon para disparar silenciosamente al vacío cuando no tenían suscriptores? (Supongo que puedes hacerlo manualmente agregando explícitamente un delegado vacío).
El patrón recomendado es (.net 2.0+)
public class MyClass
{
public event EventHandler<EventArgs> MyEvent; // the event
// protected to allow subclasses to override what happens when event raised.
protected virtual void OnMyEvent(object sender, EventArgs e)
{
// prevent race condition by copying reference locally
EventHandler<EventArgs> localHandler = MyEvent;
if (localHandler != null)
{
localHandler(sender, e);
}
}
public void SomethingThatGeneratesEvent()
{
OnMyEvent(this, EventArgs.Empty);
}
}
Veo muchas recomendaciones para un delegado vacío {} en un inicializador, pero estoy totalmente en desacuerdo. Si sigues el patrón anterior, solo verificas el event != null
en un solo lugar. El inicializador de delegado vacío {} es un desperdicio porque es una llamada extra por evento, desperdicia memoria y aún puede fallar si MyEvent se estableció en nulo en otro lugar de mi clase.
* Si su clase está sellada, no haría OnMyEvent()
virtual.
Gracias por las respuestas. Entiendo por qué ocurre NullReferenceException y cómo evitarlo.
Gishu dijo
¿Cuál es el punto de plantear un evento si nadie está escuchando?
Bueno, tal vez es una terminología. El atractivo de un sistema de "evento" me parece que toda la responsabilidad de las consecuencias del evento que tuvo lugar debe recaer en los observadores y no en el artista intérprete o ejecutante.
Quizás una pregunta mejor es: si se declara un campo delegado con la palabra clave event delante de él, ¿por qué el compilador no traduce todas las instancias de:
MadeSound(this, EventArgs.Empty)
a
if (MadeSound != null) { MadeSound(this, EventArgs.Empty); }
detrás de escena de la misma manera que otros atajos de sintaxis son? El número de métodos de comprobación nula OnSomeEvent repetitivos que las personas deben escribir manualmente debe ser colosal.
James proporcionó un buen razonamiento técnico. También me gustaría añadir que he visto a las personas usar esta ventaja, si ningún suscriptor está escuchando un evento, tomarán medidas para registrarlo en el código o algo similar. Un ejemplo simple, pero apropiado en este contexto.
Muy Zen, ¿eh?
Debes probar null cuando quieras plantear un evento:
protected void OnMyEvent()
{
if (this.MyEvent != null) this.MyEvent(this, EventArgs.Empty);
}
Sería bueno si no tuvieras que molestarte con esto, pero ellos son los descansos.
Necesitas entender lo que está haciendo tu declaración de evento. Está declarando tanto un evento como una variable. Cuando te refieres a él dentro de la clase, solo te estás refiriendo a la variable, que será nula cuando no haya suscriptores.
Otra buena manera que he visto para evitar esto, sin tener que recordar comprobar nulo:
class Tree
{
public event EventHandler MadeSound = delegate {};
public void Fall() { MadeSound(this, new EventArgs()); }
static void Main(string[] args)
{
Tree oaky = new Tree();
oaky.Fall();
}
}
Tenga en cuenta al delegado anónimo, probablemente un pequeño golpe de rendimiento, por lo que debe averiguar qué método (verificar nulo o delegado vacío) funciona mejor en su situación.
¿Cuál es el punto de plantear un evento si nadie está escuchando? Técnicamente, es solo cómo C # eligió implementarlo.
En C #, un evento es un delegado con algunas plumas especiales. Un delegado en este caso se puede ver como una lista vinculada de indicadores de función (para manejar métodos de suscriptores). Cuando "enciendes el evento", se invoca cada puntero a la vez. Inicialmente, el delegado es un objeto nulo como cualquier otra cosa. Cuando se hace un + = para la primera acción de suscripción, se llama a Delegate.Combine, que crea una instancia de la lista. (Llamar a null.Invoke () arroja la excepción nula, cuando se dispara el evento).
Si aún sientes que "no debe ser así", utiliza una clase de ayuda EventosHelper como se menciona aquí con la ''publicación de eventos defensivos'' anterior y mejorada http://weblogs.asp.net/rosherove/articles/DefensiveEventPublishing.aspx
Usar un método de extensión sería útil en este escenario.
public static class EventExtension
{
public static void RaiseEvent<T>(this EventHandler<T> handler, object obj, T args) where T : EventArgs
{
if (handler != null)
{
handler(obj, args);
}
}
}
Luego se puede usar como a continuación.
public event EventHandler<YourEventArgs> YourEvent;
...
YourEvent.RaiseEvent(this, new YourEventArgs());