从事件中删除重复的委托

Maa*_*anu 5 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 });
    }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*Tao 10

原始方法存在问题

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

  1. Dictionary<Delegate, Delegate>每个方法调用上构造并填充新的.这很浪费.
  2. 您使用的DynamicInvoke开头性能比常规调用慢.
  3. 您构建一个新的object[]作为参数传递给您的DynamicInvoke呼叫,再次在每次 FireEvent呼叫时.

这有点是已建立的事件处理机制的腐败.

建议改进方法

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

private HashSet<EventHandler> _pausedHandlers = new HashSet<EventHandler>();

public event EventHandler Paused
{
    add // will not add duplicates
    { _pausedHandlers.Add(value); }

    remove
    { _pausedHandlers.Remove(value); }
}
Run Code Online (Sandbox Code Playgroud)

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

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

关于"重复代表"概念的说明

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

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");
    //}
}
Run Code Online (Sandbox Code Playgroud)