防止相同的事件处理程序分配多次

如果我在运行时分配一个事件处理程序,并且它处于一个可以多次调用的地方,build议的做法是防止同一个处理程序对同一个事件的多个分配。

object.Event += MyFunction 

在不止一次调用的地方添加这个函数会执行处理程序“n”次(当然)。

在尝试添加via之前,我已经采取了删除任何以前的处理程序

 object.Event -= MyFunction; object.Event += MyFunction; 

这工作,但似乎不知何故。 有关正确处理的任何build议;)。

Baget对于使用明确实现的事件是正确的(尽pipe有显式接口实现和完整事件语法的混合)。 你可能会逃避这个:

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

这可能会有一些奇怪的边缘情况,如果你添加或删除多播委托,但这是不可能的。 它也需要仔细的文件,因为这不是事件正常工作的方式。

我倾向于在一次执行的path中添加事件处理程序,例如在构造函数中。

您可以实现自己的分配存储,并在将其添加到事件时检查唯一性。 一个例子见下面的EventOwner2类。 我不知道这是如何performance明智,但再次,这并不总是一个问题。

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

什么是“对象”的访问修饰符?

如果它是私有的,你只需要担心包含对象设置事件处理程序。 如果是内部的,则只需要担心包含的组件设置事件处理程序。 如果是公开的,那么它是开放的。

如果可以在包含的类上使“object”变为private,则可以通过控制本地类中的事件处理程序分配来使检查更有效。

如果需要“内部”或“公共”和唯一性,则使用隐藏“对象”的包装类,而是暴露一个方法来指定一个事件处理程序,并在其后面使用您的检查来确保唯一性。