2008-12-30 10 views
44

Si estoy asignando un controlador de eventos en tiempo de ejecución y está en un lugar que se puede llamar varias veces, ¿cuál es la práctica recomendada para evitar asignaciones múltiples del mismo controlador para el mismo evento.Prevención de la misma asignación de controlador de eventos varias veces

object.Event += MyFunction 

Agregar esto en un lugar que se llamará más de una vez ejecutará el controlador 'n' veces (por supuesto).

he recurrido a la eliminación de cualquier controlador anterior antes de intentar añadir través

object.Event -= MyFunction; 

object.Event += MyFunction; 

Esto funciona, pero parece fuera de alguna manera. Cualquier sugerencia sobre el manejo adecuado;) de este escenario.

+1

Su solución de eliminar el evento primero y luego agregar es en realidad una respuesta de alta calificación para http://stackoverflow.com/questions/937181/c-sharp-pattern-to-prevent-an-event-handler-hooked- dos veces (esta cuestión también tiene más respuestas a este tema) – OneWorld

+0

posible duplicado de [Cómo garantizar un evento sólo está suscrito a una vez] (http://stackoverflow.com/questions/367523/how-to-ensure-an-event -is-only-suscrito a la vez) –

Respuesta

36

Baget tiene razón sobre el uso de un evento explícitamente implementado (aunque hay una mezcla de implementación de interfaz explícita y la sintaxis completa del evento). Usted probablemente puede salirse con la suya:

private EventHandler foo; 

public event EventHandler Foo 
{ 
    add 
    { 
     // First try to remove the handler, then re-add it 
     foo -= value; 
     foo += value; 
    } 
    remove 
    { 
     foo -= value; 
    } 
} 

que pueden tener algunos casos extremos impares si alguna vez agregar o quitar los delegados de multidifusión, pero eso es poco probable. También necesita documentación cuidadosa, ya que no es la forma en que normalmente funcionan los eventos.

+0

para evitar el error del compilador, cuando se dispara el evento utilice el nombre de 'privado' del evento. 'foo' vs 'Foo' – Zyo

4

Tiendo a agregar un controlador de eventos en una ruta que se ejecuta una vez, por ejemplo en un constructor.

2

Puede implementar su propio almacenamiento de los delgados y verificar la exclusividad al agregarlos al evento. Consulte la clase EventOwner2 a continuación para ver un ejemplo. No sé cómo esto está haciendo a rendimiento, pero de nuevo, esto no es siempre un problema.

using System; 
using System.Collections.Generic; 

namespace EventExperiment 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IEventOwner e=new EventOwner2(); 
      Subscriber s=new Subscriber(e); 
      e.RaiseSome(); 
      Console.ReadKey(); 
     } 
    } 

    /// <summary> 
    /// A consumer class, subscribing twice to the event in it's constructor. 
    /// </summary> 
    public class Subscriber 
    { 
     public Subscriber(IEventOwner eventOwner) 
     { 
      eventOwner.SomeEvent += eventOwner_SomeEvent; 
      eventOwner.SomeEvent += eventOwner_SomeEvent; 
     } 

     void eventOwner_SomeEvent(object sender, EventArgs e) 
     { 
      Console.WriteLine(DateTimeOffset.Now); 
     } 

    } 

    /// <summary> 
    /// This interface is not essensial to this point. it is just added for conveniance. 
    /// </summary> 
    public interface IEventOwner 
    { 
     event EventHandler<EventArgs> SomeEvent; 
     void RaiseSome(); 
    } 

    /// <summary> 
    /// A traditional event. This is raised for each subscription. 
    /// </summary> 
    public class EventOwner1 : IEventOwner 
    { 
     public event EventHandler<EventArgs> SomeEvent = delegate { }; 
     public void RaiseSome() 
     { 
      SomeEvent(this,new EventArgs()); 
     } 
    } 
    /// <summary> 
    /// A custom event. This is raised only once for each subscriber. 
    /// </summary> 
    public class EventOwner2 : IEventOwner 
    { 
     private readonly List<EventHandler<EventArgs>> handlers=new List<EventHandler<EventArgs>>(); 
     public event EventHandler<EventArgs> SomeEvent 
     { 
      add 
      { 
       lock (handlers) 
        if (handlers!=null&&!handlers.Contains(value)) 
        { 
         handlers.Add(value); 
        } 
      } 
      remove 
      { 
       handlers.Remove(value); 
      } 
     } 
     public void RaiseSome() 
     { 
      EventArgs args=new EventArgs(); 
      lock(handlers) 
      foreach (EventHandler<EventArgs> handler in handlers) 
      { 
       handler(this,args); 
      } 
     } 
    } 
} 
0

¿Qué es el modificador de acceso de 'objeto'?

Si es privado, sólo tiene que preocuparse por la configuración del controlador de eventos objeto que contiene. Si es interno, sólo tendrá que preocuparse por el conjunto que contiene la configuración del controlador de eventos. si es público, entonces es muy abierto.

Si 'object' se puede hacer privado en la clase contenedora, puede hacer que sus comprobaciones sean mucho más eficientes controlando la asignación del controlador de eventos en la clase local.

Si es 'interno' o 'público' y unicidad, vaya con una clase contenedora que oculte 'objeto' y en su lugar expone un método para asignar un controlador de eventos con sus controles detrás para garantizar la exclusividad.

Cuestiones relacionadas