2008-11-14 14 views
38

tengo una clase estática que me gustaría plantear un evento como parte de un bloque intento de captura dentro de un método estático de la clase.Cómo elevar evento personalizado a partir de una clase estática

Por ejemplo, en este método me gustaría plantear un evento personalizado en la captura.

public static void saveMyMessage(String message) 
{ 
    try 
    { 
     //Do Database stuff 
    } 
    catch (Exception e) 
     { 
       //Raise custom event here 
     } 
} 

Gracias.

Respuesta

88

Importante: tener mucho cuidado sobre la suscripción a un evento estática de los casos. Estático a estático está bien, pero una suscripción de un evento estático a un controlador de instancias es una gran forma (es decir, muy peligrosa) de mantener viva esa instancia para siempre. GC verá el enlace y no recopilará la instancia a menos que se desinscriba (o use algo como una WeakReference).

El patrón para la creación de eventos estáticos es lo mismo que los acontecimientos isntance, sólo con static:

public static event EventHandler SomeEvent; 

para hacer la vida más fácil (re comprobación nula), un truco útil es para agregar un controlador trivial:

public static event EventHandler SomeEvent = delegate {}; 

A continuación, puede invocar sin el cheque nulo:

SomeEvent(null, EventArgs.Empty); 

Tenga en cuenta que debido a que las instancias de delegado son inmutables, y la eliminación de referencias es segura para subprocesos, nunca hay una condición de carrera aquí, y no es necesario bloquear ... quien sea que esté suscrito cuando se desvíe la invocación.

(ajuste para su propio evento-args, etc.). Este truco se aplica por igual a eventos de instancia.

+0

Al leer su respuesta, indicó: "una suscripción de un evento estático a un controlador de instancias es una excelente manera de mantener viva esa instancia para siempre", supongo que esto no siempre será óptimo. Si, por ejemplo, estoy usando una clase estática para almacenar la configuración del formulario mientras se está ejecutando, esto no será tan malo ya que es estático y no puede tener varias copias. (Programador novato, perdona la mala terminología) – Josh

+0

@Josh "la gran manera" se entendía como "una forma realmente peligrosa" - Lo he aclarado. Si solo tiene una suscripción, no es un gran problema, independientemente de la instancia estática frente a la instancia. La parte peligrosa viene cuando cada instancia se suscribe (digamos) a un evento estático, o un evento en un objeto de larga vida, y nunca cancela la suscripción. Hey presto: memoria obstruida. –

+1

"suscripción de un evento estático a un manejador de instancias" ¿está escrito así a propósito? Creo que debería ser "suscripción de un controlador de instancias un evento estático" en su lugar. –

0

Nota: VS2008, C#

Sólo declarar un evento como lo haría normalmente dentro de la clase estática, pero asegúrese de marcar el evento como estática:

public static event EventHandler Work; 

A continuación, sólo se suscriba a él a medida que normalmente lo haría.

+0

Re "como lo haría normalmente" - debe ser más cauteloso con los eventos estáticos, especialmente con la cancelación de la suscripción. Eso funcionará en cualquier versión de C# por cierto. –

10

Su caso también tendría que ser estática:

public class ErrorEventArgs : EventArgs 
{ 
    private Exception error; 
    private string message; 

    public ErrorEventArgs(Exception ex, string msg) 
    { 
     error = ex; 
     message = msg; 
    } 

    public Exception Error 
    { 
     get { return error; } 
    } 

    public string Message 
    { 
     get { return message; } 
    } 
} 

public static class Service 
{ 
    public static EventHandler<ErrorEventArgs> OnError; 

    public static void SaveMyMessage(String message) 
    { 
      EventHandler<ErrorEventArgs> errorEvent = OnError; 
     if (errorEvent != null) 
     { 
      errorEvent(null, new ErrorEventArgs(null, message)); 
     } 
    } 
} 

Y Uso:

public class Test 
{ 
    public void OnError(object sender, ErrorEventArgs args) 
    { 
     Console.WriteLine(args.Message); 
    } 
} 

Test t = new Test(); 
Service.OnError += t.OnError; 
Service.SaveMyMessage("Test message"); 
+0

+1 Ha pasado tanto tiempo desde que tuve que escribir el manejo de eventos que ni siquiera podía recordar su sintaxis. Aparentemente porque solo podía pensar en la sintaxis 2.0 (la última vez que tuve que escribir una) y olvidé que la agregaron en 3.5 –

6

Varias personas han ofrecido ejemplos de código, simplemente no desencadenar un evento usando código como:

if(null != ExampleEvent) 
{ 
    ExampleEvent(/* put parameters here, for events: sender, eventArgs */); 
} 

como esto contiene una condición de carrera entre cuando se marca el evento para nulo y cuando en realidad disparar el evento. En lugar de utilizar una simple variación:

MyEvent exampleEventCopy = ExampleEvent; 
if(null != exampleEventCopy) 
{ 
    exampleEventCopy(/* put parameters here, for events: sender, eventArgs */); 
} 

Esto copiará ningún suscriptores de eventos en el exampleEventCopy, que luego se puede utilizar como un sólo local versión del evento público sin tener que preocuparse por las condiciones de carrera (En esencia, se Es posible que otro subproceso pueda adelantarse inmediatamente después de haber comprobado el evento público como nulo y proceda a eliminar a todos los suscriptores del evento, lo que provoca que la activación posterior del evento genere una excepción al usar una copia local solamente. se evita la posibilidad de que otro hilo elimine suscriptores, ya que no hay forma de que puedan acceder a la variable local).

+7

Una solución más fácil es: evento estático público EventHandler Work = delegate {}; Ahora nunca es nulo y puedes invocarlo. Ligeramente vago, pero no lo suficiente como para lastimar. –

+1

@Mark En comparación con copiar la lista de delegados y hacer una comprobación nula, creo que tener un delegado de "no hacer nada" es más rápido y sencillo. Gracias por la respuesta anterior. – MindJuice

0

Solo para agregar "Los delegados son inmutables" Por lo tanto, como se muestra en el ejemplo anterior, la siguiente línea obtiene una copia del delegado.

EventHandler<ErrorEventArgs> errorEvent = OnError; 
Cuestiones relacionadas