views:

2483

answers:

5

I'm sure that i'm just not understanding something fundamental about events and/or delegates in C#, but why can't i do the Boolean tests in this code sample:

public class UseSomeEventBase {
    public delegate void SomeEventHandler(object sender, EventArgs e);
    public event SomeEventHandler SomeEvent;
    protected void OnSomeEvent(EventArgs e) {
        // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
        if (SomeEvent != null) SomeEvent(this, e);
    }
}

public class UseSomeEvent : UseSomeEventBase {
    public bool IsSomeEventHandlerNull() {
        // "LEFT HAND SIDE" COMPILER ERROR
        return SomeEvent == null;
    }
}

class Program {
    static void Main(string[] args) {
        var useSomeEvent = new UseSomeEvent();
        useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEvent.SomeEvent == null) {

        }
        var useSomeEventBase = new UseSomeEventBase();
        useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEventBase.SomeEvent == null) {

        }
    }

    static void FuncToHandle(object sender, EventArgs e) { }
}
+15  A: 

An event is really just an "add" operation and a "remove" operation. You can't get the value, you can't set the value, you can't call it - you can just subscribe a handler for the event (add) or unsubscribe one (remove). This is fine - it's encapsulation, plain and simple. It's up to the publisher to implement add/remove appropriately, but unless the publisher chooses to make the details available, subscribers can't modify or access the implementation-specific parts.

Field-like events in C# (where you don't specify the add/remove bits) hide this - they create a variable of a delegate type and an event. The event's add/remove implementations just use the variable to keep track of the subscribers.

Inside the class you refer to the variable (so you can get the currently subscribed delegates, execute them etc) and outside the class you refer to the event itself (so only have add/remove abilities).

The alternative to field-like events is where you explicitly implement the add/remove yourself, e.g.

private EventHandler clickHandler; // Normal private field

public event EventHandler Click
{
    add
    {
        Console.WriteLine("New subscriber");
        clickHandler += value;
    }
    remove
    {
        Console.WriteLine("Lost a subscriber");
        clickHandler -= value;
    }
}

See my article on events for more information.

Of course the event publisher can also make more information available - you could write a property like ClickHandlers to return the current multi-cast delegate, or HasClickHandlersto return whether there are any or not. That's not part of the core event model though.

Jon Skeet
+1 for the answer. Just to add to it, you can, of course, implement the add/remove operations as explicit methods with a multi-cast delegate internally. In which case, you can track subscribers and un-subscribers yourself - and thus provide access to additional information (e.g. count of subscribers, etc) - if you so choose.
LBushkin
Yup - will add that, thanks.
Jon Skeet
It might be useful to note that the *reason* events are implemented this way is so that any class cannot willy-nilly change the behaviour of the class with the event by simply accessing the event (like a delegate property) and wiping out it's list of subscribers.
womp
@womp: I think that's covered by the encapsulation side - and my linked article. Will add a sentence to the first paragraph to make sure.
Jon Skeet
should be "... public **event** EventHandler Click ..."
thorn
@thorn: Thanks, fixed.
Jon Skeet
A: 

You'd have to do that from the base class. That's the exact reason that you did this:

protected void OnSomeEvent(EventArgs e) {
    // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
    if (SomeEvent != null) SomeEvent(this, e);
}

You can't access events from a derived class. Also, you should make that method virtual, so that it can be overridden in a derived class.

Darthg8r
Be aware that that's not threadsafe. It can throw a NullReferenceException if the last handler is removed after the test but before the invocation.
Jon Skeet
@Jon - i did read your article about this, but i don't think this would happen in this web app.
gabe
+1  A: 

Here's a slightly different question

What value is there in testing an externally defined event for null?

As an external consumer of an event you can only do 2 operations

  • Add a handler
  • Remove a handler

The null or non-nullness of the event has no bearing on these 2 actions. Why do you want to run a test which provides no perceivable value?

JaredPar
i am trying to write a wrapper work-around class for 2 poorly designed third-party web controls that i am required to use for a project (i hate this crazy controls). i'm trying to enforce the use of a specific method as the event handler for an event in a member object (reference to one of the web controls) of a class (the wrapper). i want to do a check in the wrapper class to ensure that user of the wrapper class has added a specific event handler to an event of the member object. it's very specific to this 2 stupid controls and hard to explain...
gabe
A: 

It's a rule in place when using the 'event' keyword. When you create an event, you are restricting outside class interaction with the delegate to a "subscribe / unsubscribe" relationship, this includes cases of inheritance. Remember an event is essentially a property, but for method calls, it isn't really an object itself, so really it looks more like this:

public event SomeEventHandler SomeEvent
{
     add
     {
          //Add method call to delegate
     }
     remove
     {
          //Remove method call to delegate
     }
}
Zensar
A: 

I have a use case where i have to unsubscribe the event but prior to this i want to check this guy has subscribed to it or not

How can i find out before unsubscribing for an event whether somebody has subscribed to this event or not ?

Please help me out .

Amit
You must be new here. You'll need to ask your own question.
Charlie Salts