How to correctly unregister an event handler

前端 未结 2 1500
滥情空心
滥情空心 2020-11-29 20:00

In a code review, I stumbled over this (simplified) code fragment to unregister an event handler:

 Fire -= new MyDelegate(OnFire);

I though

2条回答
  •  悲哀的现实
    2020-11-29 20:25

    You should always check whether a delegate has no targets (its value is null) before firing it. As said before, one way of doing this is to subscribe with a do-nothing anonymous method which won't be removed.

    public event MyDelegate Fire = delegate {};
    

    However, this is just a hack to avoid NullReferenceExceptions.

    Just simply cheking whether a delegate is null before invoking is not threadsafe as an other thread can deregister after the null-check and making it null when invoking. There is an other solution is to copy the delegate into a temporary variable:

    public event MyDelegate Fire;
    public void FireEvent(string msg)
    {
        MyDelegate temp = Fire;
        if (temp != null)
            temp(msg);
    }
    

    Unfortunately, the JIT compiler may optimize the code, eliminate the temporary variable, and use the original delegate. (as per Juval Lowy - Programming .NET Components)

    So to avoid this problem, you could use method which accepts a delegate as parameter:

    [MethodImpl(MethodImplOptions.NoInlining)]
    public void FireEvent(MyDelegate fire, string msg)
    {
        if (fire != null)
            fire(msg);
    }
    

    Note that without the MethodImpl(NoInlining) attribute the JIT compiler could inline the method making it worthless. Since delegates are immutable this implementation is threadsafe. You could use this method as:

    FireEvent(Fire,"Hello 3");
    

提交回复
热议问题