C# 防止多次分配同一事件处理程序

C# 防止多次分配同一事件处理程序,c#,events,C#,Events,如果我在运行时分配一个事件处理程序,并且它位于一个可以多次调用的位置,那么建议采取什么措施来防止将同一个处理程序多次分配给同一个事件 object.Event += MyFunction 在一个将被多次调用的点中添加它将执行处理程序'n'次(当然) 在尝试添加via之前,我已尝试删除任何以前的处理程序 object.Event -= MyFunction; object.Event += MyFunction; 这是可行的,但似乎有点不对劲。关于正确处理的任何建议;)我倾向于在一个执行一

如果我在运行时分配一个事件处理程序,并且它位于一个可以多次调用的位置,那么建议采取什么措施来防止将同一个处理程序多次分配给同一个事件

object.Event += MyFunction
在一个将被多次调用的点中添加它将执行处理程序'n'次(当然)

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

object.Event -= MyFunction; 

object.Event += MyFunction;

这是可行的,但似乎有点不对劲。关于正确处理的任何建议;)我倾向于在一个执行一次的路径中添加一个事件处理程序,例如在构造函数中。

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

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

您可以实现自己的delgate存储,并在将它们添加到事件时检查其唯一性。有关示例,请参见下面的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);
            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
名称空间事件实验
{
班级计划
{
静态void Main(字符串[]参数)
{
IEventOwner e=新事件所有者2();
订户s=新订户(e);
e、 提升();
Console.ReadKey();
}
}
/// 
///一个使用者类,在其构造函数中订阅两次事件。
/// 
公共类订户
{
公共订户(IEventOwner事件所有者)
{
eventOwner.SomeEvent+=eventOwner\u SomeEvent;
eventOwner.SomeEvent+=eventOwner\u SomeEvent;
}
void eventOwner\u SomeEvent(对象发送方,事件参数e)
{
Console.WriteLine(DateTimeOffset.Now);
}
}
/// 
///这个界面在这一点上并不重要。它只是为了方便而添加的。
/// 
公共接口IEventOwner
{
事件处理程序SomeEvent;
虚空上升();
}
/// 
///传统事件。这是为每个订阅引发的。
/// 
公共类事件所有者1:IEventOwner
{
public event EventHandler SomeEvent=delegate{};
公共无效理由
{
SomeEvent(这是新的EventArgs());
}
}
/// 
///自定义事件。对于每个订阅服务器,此事件仅引发一次。
/// 
公共类事件所有者2:IEventOwner
{
私有只读列表处理程序=新列表();
公共事件事件处理程序SomeEvent
{
添加
{
锁(处理程序)
if(handlers!=null&&!handlers.Contains(value))
{
添加(值);
}
}
去除
{
移除(值);
}
}
公共无效理由
{
EventArgs args=新的EventArgs();
锁(处理程序)
foreach(处理程序中的EventHandler)
{
处理程序(this,args);
}
}
}
}

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

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

如果“object”可以在包含类上私有化,那么可以通过控制本地类中的事件处理程序分配来提高检查效率


如果需要“内部”或“公共”和唯一性,则使用一个隐藏“对象”的包装类,而不是公开一个分配事件处理程序的方法,并在其后面进行检查以确保唯一性。

您先删除事件,然后再添加事件的解决方案实际上是一个很好的解决方案(此问题对此主题还有更多答案)的可能重复为避免编译器错误,在触发事件时使用事件的“private”名称。“foo”与“foo”