tags:

views:

1987

answers:

3

I have declared a generic event handler

public delegate void EventHandler();

to which I have added the extension method 'RaiseEvent':

public static void RaiseEvent(this EventHandler self)        {
   if (self != null) self.Invoke();
}

When I define the event using the typical syntax

public event EventHandler TypicalEvent;

then I can call use the extension method without problems:

TypicalEvent.RaiseEvent();

But when I define the event with explicit add/remove syntax

private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent {
   add { _explicitEvent += value; } 
   remove { _explicitEvent -= value; }
}

then the extension method does not exist on the event defined with explicit add/remove syntax:

ExplicitEvent.RaiseEvent(); //RaiseEvent() does not exist on the event for some reason

And when I hover over to event to see the reason it says:

The event 'ExplicitEvent' can only appear on the left hand side of += or -=

Why should an event defined using the typical syntax be different from an event defined using the explicit add/remove syntax and why extension methods do not work on the latter?

EDIT: I found I can work around it by using the private event handler directly:

_explicitEvent.RaiseEvent();

But I still don't understand why I cannot use the event directly like the event defined using the typical syntax. Maybe someone can enlighten me.

+13  A: 

Because you can do this (its non-real-world sample, but it "works"):

private EventHandler _explicitEvent_A;
private EventHandler _explicitEvent_B;
private bool flag;
public event EventHandler ExplicitEvent {
   add {
         if ( flag = !flag ) { _explicitEvent_A += value; /* or do anything else */ }
         else { _explicitEvent_B += value; /* or do anything else */ }
   } 
   remove {
         if ( flag = !flag ) { _explicitEvent_A -= value; /* or do anything else */ }
         else { _explicitEvent_B -= value; /* or do anything else */ }
   }
}

How can compiler know what he should do with "ExplicitEvent.RaiseEvent();" ? Answer: He can't.

The "ExplicitEvent.RaiseEvent();" is only syntax sugar, which can be predicated only if is event implicitly implemented.

TcKs
How do you know that the compiler is a "He"? Just kidding, thanks for the answer and the sample which explains it really really well. I love this site for blazing fast answers like this.
Hermann
+1 the blazing fast thing. It's what got me coming back. That and... the immense amount of knowledge.
MrZombie
+2  A: 

The "plain" declaration for TypicalEvent does some compiler trickery. It creates an event metadata entry, add and remove methods and a backing field. When your code refers to TypicalEvent, the compiler translates it into a reference to the backing field; when external code refers to TypicalEvent (using += and -=), the compiler translates it into a reference to the add or remove method.

The "explicit" declaration bypasses this compiler trickery. You are spelling out the add and remove methods and the backing field: indeed, as TcKs points out, there may not even be a backing field (this is a common reason for using the explicit form: see e.g. events in System.Windows.Forms.Control). Therefore the compiler can no longer quietly translate the reference to TypicalEvent into a reference to the backing field: if you want the backing field, the actual delegate object, you have to reference the backing field directly:

_explicitEvent.RaiseEvent()
itowlson
Not quite - within the class, += and -= will *still* refer to the field. It refers to the event when you refer to it from outside the class.
Jon Skeet
Thanks for your answer. You explained it very well. I already found about the workaround myself, but still thanks for the sample on how I would call RaisEvent() on the explicit event.
Hermann
Jon, thanks for the insight. Always good to remember the intricacies of using events.
Hermann
Thanks Jon -- I've corrected the post.
itowlson
+9  A: 

When you create a "field-like" event, like this:

public event EventHandler Foo;

the compiler generates a field and an event. Within the source code of the class which declares the event, any time you refer to Foo the compiler understand that you're referring to the field. However, the field is private, so any time you refer to Foo from other classes, it refers to the event (and therefore the add/remove code).

If you declare your own explicit add/remove code, you don't get an auto-generated field. So, you've only got an event, and you can't raise an event directly in C# - you can only invoke a delegate instance. An event isn't a delegate instance, it's just an add/remove pair.

Now, your code contained this:

public EventHandler TypicalEvent;

This is slightly different still - it wasn't declaring an event at all - it was declaring a public field of the delegate type EventHandler. Anyone can invoke that, because the value is just a delegate instance. It's important to understand the difference between a field and an event. You should never write this kind of code, just as I'm sure you don't normally have public fields of other types such as string and int. Unfortunately it's an easy typo to make, and a relatively hard one to stop. You'd only spot it by noticing that the compiler was allowing you to assign or use the value from another class.

See my article on events and delegates for more information.

Jon Skeet
Thanks Jon, I already read your article some time ago, it just wasn't immediatelly clear to me when I noticed this "odd behaviour" (to me at that time) with the extension method.
Hermann