如果我在运行时分配事件处理程序并且它位于可以多次调用的位置,那么建议的做法是阻止同一处理程序多次分配给同一事件。
object.Event += MyFunction
在一个将被多次调用的位置添加它将执行处理程序'n'次(当然)。
在尝试通过
添加之前,我已经尝试删除任何先前的处理程序object.Event -= MyFunction;
object.Event += MyFunction;
这可行,但似乎不知何故。关于正确处理的任何建议;)这种情况。
答案 0 :(得分:37)
Baget关于使用显式实现的事件是正确的(尽管存在显式接口实现和完整事件语法的混合)。你可以逃脱这个:
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;
}
}
如果您添加或删除多播委托,那可能会有一些奇怪的边缘情况,但这不太可能。它还需要仔细记录,因为它不是事件正常工作的方式。
答案 1 :(得分:4)
我倾向于在一次执行的路径中添加一个事件处理程序,例如在构造函数中。
答案 2 :(得分:2)
您可以实现自己的delgates存储,并在将其添加到事件时检查唯一性。有关示例,请参阅下面的EventOwner2类。我不知道这是如何做到性能明智的,但不仅如此,这并不总是一个问题。
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);
}
}
}
}
答案 3 :(得分:0)
'object'的访问修饰符是什么?
如果它是私有的,您只需要担心设置事件处理程序的包含对象。 如果它是内部的,您只需要担心包含程序集设置事件处理程序。 如果它是公开的,那么它是敞开的。
如果'object'可以在包含的类上变为私有,则可以通过控制本地类中的事件处理程序赋值来提高检查效率。
如果要求'内部'或'公共'和唯一性,请使用隐藏'对象'的包装类,而是公开一个方法,用于为事件处理程序分配支持,以确保其唯一性。