简体   繁体   中英

Why can I check some event handlers for null, some not?

I have a ugly piece of code that adds event handlers. The problem is, if the code is called multiple times, the event handlers are called multiple times.

To solve the problem, I remove the event handler first and then add it.

Now I've seen the following behaviour:

Some event handlers can be checked like:

if (object.event == null) {
    //
    // Code
    //
}

others of the form

if (object.object.event == null) {
    //
    // Code
    //
}

I get a message like 'object.object.event' may only occur left of -= or +=. (Since I'm using a german version of visual studio, I don't know the correct translation to english).

I have no idea why the behaviour looks this inconsequent so I would be grateful for some information on this.

To be more specific: It's user control.

if (myControl.Event == null) {
    //
    // works
    //
}

if (myControl.TreeView.NodeMouseClick == null) {
    //
    // doesn't work
    //
}

To solve the problem, I remove the event handler first and then add it.

That doesn't solve the problem. The event keyword provides accessors for a delegate object. Much like a property provides accessors for a field. On a property, you always need one get or set . An event has the add, remove and raise accessors. But the compiler will generate a default implementation for them if you don't do so yourself. Which is fairly common.

The advantage of a property accessor is that the backing field can be private. Nobody can mess with it, except the class that contains the field. All access has to go through the get and set accessors. The event keyword works exactly the same way, nobody can mess with the delegate object, except the code in the class that contains the event.

Which shoots a gaping hole in your attempt to avoid raising the event. You cannot mess with the list of subscribers for an event that's declared in another class, the compiler is telling you this. The normal way this is done is setting a bool flag to indicate that events have to be temporarily ignored. The event handler can check that flag and avoid executing any side-effects.

You can only access the backing field for an event defined in your class.
For more information, see the spec . (Although this has changed in C# 4, the changes are irrelevant to you)

Best practice in your case would be to create a protected internal On EventName method in each class.

SLaks is correct, and has linked to some excellent resources. Here's a relevant quote from Chris Burrows' blog article :

Let me take a quick detour here and explain to you how the binding of += works in C#. There are two possibilities:

  1. either there is an actual + operator, such as with ints, and x += y binds to “x = x + y” except that x is only evaluated once. This is the compound assignment operator; or
  2. the thing on the left hand side is an event, and xE += y binds to “x.add_E(y)”. This is the event accessor operator, and in fact this is the only way to bind to an event accessor.

So what do we have in the snippet above? Well, the extra bit of detail that you need to decide is the following rule about field-like events in C#: outside of the class or struct that defines a field-like event E, binding to the name E resolves to the event itself, on which the only legal operation is calling an accessor; inside the class or struct that defines a field-like event E, binding to the name E resolves to the private delegate field .

In your case, when resolving myControl.Event , you're inside the myControl class, so you don't see an event object; instead you see an actual delegate object, which you can compare with null. When resolving myControl.TreeView.NodeMouseClick , you're outside the TreeView class, so you can't access the actual delegate object; all you get is the event object, which cannot be compared to null.

If I understand correctly, all of this wouldn't help you anyway, since presumably after you check for null, you're going to try to fire the TreeView 's event for it, which you can't do.

Depending on what you're trying to do, you could probably subclass TreeView and add an internal method that would call the protected TreeView.OnNodeMouseClick method to fire the event.

您只能查询自己的事件处理程序以获取附加的侦听器。

Automatic events, like this one:

public event EventHandler SomethingHappened;

are implemented by the compiler using a multicast delegate.

When you write myControl.Event == null , the compiler actually needs to call Delegate.GetInvocationList on that delegate. The compiler does not let you do that unless the code is inside a method of the class exposing the event, hence the error (it only allows you to add or remove from the invocation list).

If we were talking about an event you define in your own class, then you would have the option of exposing the invocation list (eg through a method) and doing what you are trying to do. But for existing classes (eg TreeView ) it is not possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM