Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is adding (or removing) a null event listener a no-op?

Usually answers to questions like this can be found either here or, if not here, on MSDN, but I have looked and looked and not found the answer. Experimentation seems to show that code like:

SomeControl.Click += null;

does no harm. Or at least that firing the event doesn't toss an exception (that I can tell) by trying to invoke a null event listener. However, there seems to be nowhere on the Web that will validate my hope that such code isn't doing at least something that I might not want done. For example, causing the Click event to think it has listeners when it might not, and wasting those ever-precious super-ultra-mini-microseconds in "now let's perform null-checks and invoke all listeners that are not null" code.

It seems that documentation should say what the behavior of the += and -= operators is if the listener on the RHS is null. But like I say, I can't find that documentation anywhere. I'm sure someone here can provide it, though....

(Obviously, my code doesn't have a hard-coded null; my question is more about whether code like this is wasteful or completely harmless:

public static void AddHandlers([NotNull] this Button button,
                               [CanBeNull] EventHandler click = null,
                               [CanBeNull] EventHandler load = null)
{
    button.Click += click;
    button.Load += load;
}

or if I should [i.e., need to for any reason] add null-checks around each such += operation.)

like image 350
Manus Hand Avatar asked Nov 26 '15 04:11

Manus Hand


People also ask

Can I remove event listener?

JavaScript | removeEventListener() method with examplesThe removeEventListener() is an inbuilt function in JavaScript which removes an event handler from an element for a attached event. for example, if a button is disabled after one click you can use removeEventListener() to remove a click event listener.

Can events be null?

With this approach, the event always has at least one subscriber, and is, therefore, never null.

How do I remove all event listeners?

To remove all event listeners from an element: Use the cloneNode() method to clone the element. Replace the original element with the clone. The cloneNode() method copies the node's attributes and their values, but doesn't copy the event listeners.


2 Answers

Consider this code:

void Main()
{
    var foo = new Foo();
    foo.Blah += Qaz;
    foo.Blah += null;
    foo.OnBlah();
}

public void Qaz()
{
    Console.WriteLine("Qaz");
}

public class Foo
{
    public event Action Blah;
    public void OnBlah()
    {
        var b = Blah;
        if (b != null)
        {
            Console.WriteLine("Calling Blah");
            b();
            Console.WriteLine("Called Blah");
        }
    }
}

As is it runs without error and produces the following output:

Calling Blah
Qaz
Called Blah

If I remove the line foo.Blah += Qaz; then the code runs without error, but produces no output, so the null handler is effectively ignored.

As far as IL goes, the line foo.Blah += null; produces the following IL:

IL_001A:  ldloc.0     // foo
IL_001B:  ldnull      
IL_001C:  callvirt    Foo.add_Blah
IL_0021:  nop 

So, it behaves as a nop, but it clearly runs the code.

like image 113
Enigmativity Avatar answered Nov 15 '22 19:11

Enigmativity


The answer depends on whether the += (or -=) operator is being applied to an event or a delegate.

In your AddHandlers example, the Button class probably defines Click and Load as events, not delegates. When its left operand is an event, the += operator simply invokes the add accessor of the event without any additional null checks. That is, under the covers button.Click += value; is just a method call:

button.add_Click(value);

Like any other method, how add_Click deals with a null argument is entirely up to the Button class.

Now consider how the Button class might actually implement the Click event. Here's one way:

private EventHandler _click;
public event EventHandler Click
{
    add { _click += value; }
    remove { _click -= value; }
}

// Or even shorter...
// public event EventHandler Click;
// ... which is the same as above plus extra stuff for thread safety.

In this implementation, EventHandler is a delegate type. When both operands are delegates, the += operator invokes Delegate.Combine. The documentation says that Delegate.Combine(a, b) returns:

A new delegate with an invocation list that concatenates the invocation lists of a and b in that order. Returns a if b is null, returns b if a is a null reference, and returns a null reference if both a and b are null references.

Conclusion: As long as the Button class implements its events in the "standard" way (rather than, say, keeping track of listeners in an explicit List<EventHandler>), then button.Click += null; will ultimately not add a null listener.

like image 29
Michael Liu Avatar answered Nov 15 '22 18:11

Michael Liu