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

时间:2008-12-30 06:31:21

标签: c# events

如果我在运行时分配事件处理程序并且它位于可以多次调用的位置,那么建议的做法是阻止同一处理程序多次分配给同一事件。

object.Event += MyFunction

在一个将被多次调用的位置添加它将执行处理程序'n'次(当然)。

在尝试通过

添加之前,我已经尝试删除任何先前的处理程序
object.Event -= MyFunction; 

object.Event += MyFunction;

这可行,但似乎不知何故。关于正确处理的任何建议;)这种情况。

4 个答案:

答案 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'可以在包含的类上变为私有,则可以通过控制本地类中的事件处理程序赋值来提高检查效率。

如果要求'内部'或'公共'和唯一性,请使用隐藏'对象'的包装类,而是公开一个方法,用于为事件处理程序分配支持,以确保其唯一性。