从事件中删除重复的委托

时间:2010-08-19 03:51:59

标签: c#

我想从事件中重复代表重复的代表。所以我写了下面的代码。它工作正常。我的申请是一个时间批评的应用程序。是否有任何其他优化的mechansim实现相同。请帮帮我


public void FireEvent()
{
    Dictionary<Delegate, Delegate> dic = new Dictionary<Delegate, Delegate>();

    Delegate[] m = this.Paused.GetInvocationList();
    foreach (Delegate d in m)
    {
        Delegate dout;
        if (dic.TryGetValue(d, out dout))
        {                    
            continue;
        } 
        else
        {
            dic.Add(d, d);
        }
        d.DynamicInvoke(new object[2] { this, null });
    }
}

1 个答案:

答案 0 :(得分:10)

原始方法的问题

如果这确实是一个时间关键的应用程序,我强烈建议您更改代码。

  1. 您在每个方法调用上构建并填充新的Dictionary<Delegate, Delegate>。这很浪费。
  2. 您使用DynamicInvoke,其开始时的性能低于常规调用。
  3. 您构建了一个新object[]作为参数传递给DynamicInvoke来电,再次每次 FireEvent来电。
  4. 这对于已建立的事件处理机制来说有点堕落。

    改进方法的建议

    在我看来,这是一个更好的解决方案:不是让这个FireEvent方法向后弯曲以忽略已添加的重复委托,为什么不阻止委托多次附加到事件中第一名?

    private HashSet<EventHandler> _pausedHandlers = new HashSet<EventHandler>();
    
    public event EventHandler Paused
    {
        add // will not add duplicates
        { _pausedHandlers.Add(value); }
    
        remove
        { _pausedHandlers.Remove(value); }
    }
    

    然后,您可以简单地以更加传统,经过时间考验的方式举办活动,确信没有任何代表多次加入该活动。

    protected void OnPaused()
    {
        foreach (EventHandler handler in _pausedHandlers)
        {
            try
            {
                handler(this, EventArgs.Empty);
            }
            catch
            {
                // up to you what to do here
            }
        }
    }
    

    关于“重复代表”概念的注释

    对这个答案的评论已经阐明了代表平等问题,我觉得将这个答案包括在内是有益的。如果您有兴趣,请查看我编写的以下代码示例,以使此主题更容易理解。

    class Program
    {
        static void Main(string[] args)
        {
            // Even though the code for FirstHandler and SecondHandler is the same,
            // they will not (nor should they) be considered equal for the purpose
            // of detecting duplicates.
            EventHandler handler1 = FirstHandler;
            EventHandler handler2 = SecondHandler;
    
            // Since handler2 and handler3 point to the same method, on the other
            // hand, they will (and ought to) be treated as equal.
            EventHandler handler3 = SecondHandler;
    
            // So this prints 'False'...
            Console.WriteLine(handler1.Equals(handler2));
    
            // ...and this prints 'True'.
            Console.WriteLine(handler2.Equals(handler3));
    
            // Now take a look at the code for SetAnonymousHandler below. The
            // method accepts an EventHandler by reference and compares it for
            // equality to a delegate pointing to an anoymous lambda method. If the
            // two are equal, it sets the EventHandler to this new delegate and 
            // returns true; otherwise it returns false.
    
            // This prints 'True', as handler1 is not equal to the delegate
            // declared within the SetAnonymousHandler method.
            Console.WriteLine(SetAnonymousHandler(ref handler1));
    
            // HOWEVER, now it prints 'False' for a simple reason: the delegate 
            // declared in SetAnonymousHandler points to an actual method. The fact
            // that it is an anonymous method simply means that the compiler
            // automatically generates a "proper" method for it in IL (to see what
            // I mean, read the comment at the bottom of this class). So now,
            // as with handler2 and handler3 above, handler1 and the delegate
            // declared in SetAnonymousHandler point to the same method and are
            // therefore equal; the method returns false.
            Console.WriteLine(SetAnonymousHandler(ref handler1));
    
            Console.ReadLine();
        }
    
        static void FirstHandler(object sender, EventArgs e)
        {
            Console.WriteLine("Testing");
        }
    
        static void SecondHandler(object sender, EventArgs e)
        {
            Console.WriteLine("Testing");
        }
    
        static bool SetAnonymousHandler(ref EventHandler handler)
        {
            EventHandler anonymous = (sender, e) => Console.WriteLine("Testing");
    
            if (!handler.Equals(anonymous))
            {
                handler = anonymous;
                return true;
            }
            else
            {
                return false;
            }
        }
    
        // Note: When the above method is compiled, the C# compiler generates code
        // that would look something like this if translated back from IL to C#
        // (not exactly, but the point is just to illustrate that an anoymous
        // method, after compilation, is really just like a "proper"
        // -- i.e., non-anonymous -- method).
    
        //static bool SetAnonymousHandler(ref EventHandler handler)
        //{
        //    EventHandler anonymous = SetAnonymousHandler_Anonymous;
    
        //    if (handler.Equals(anonymous))
        //    {
        //        handler = anonymous;
        //        return true;
        //    }
        //    else
        //    {
        //        return false;
        //    }
        //}
    
        //static void SetAnonymousHandler_Anonymous(object sender, EventArgs e)
        //{
        //    Console.WriteLine("Testing");
        //}
    }