views:

131

answers:

4

If I have a class that raises an event, with (e.g.) FrobbingEventArgs, am I allowed to handle it with a method that takes EventArgs?

Here's some code:

class Program
{
   static void Main(string[] args)
   {
      Frobber frobber = new Frobber();
      frobber.Frobbing += FrobberOnFrobbing;
      frobber.Frob();
   }

   private static void FrobberOnFrobbing(object sender,
       EventArgs e)
   {
      // Do something interesting. Note that the parameter is 'EventArgs'.
   }
}

internal class Frobber
{
   public event EventHandler<FrobbingEventArgs> Frobbing;
   public event EventHandler<FrobbedEventArgs> Frobbed;

   public void Frob()
   {
      OnFrobbing();

      // Frob.

      OnFrobbed();
   }

   private void OnFrobbing()
   {
      var handler = Frobbing;
      if (handler != null)
         handler(this, new FrobbingEventArgs());
   }

   private void OnFrobbed()
   {
      var handler = Frobbed;
      if (handler != null)
         handler(this, new FrobbedEventArgs());
   }
}

internal class FrobbedEventArgs : EventArgs { }
internal class FrobbingEventArgs : EventArgs { }

The reason I ask is that ReSharper seems to have a problem with (what looks like) the equivalent in XAML, and I'm wondering if it's a bug in ReSharper, or a mistake in my understanding of C#.

+1  A: 

Yes you can but when you access the e parameter in the event handler you will only be able to access the members that belong to EventArgs base class unless you cast it as the derived type. I think the word is polymorphic rather than covariant and all classes in c# are pollymorphic, it is a language feature.

Ben Robinson
The fact that FrobbedEventArgs can be casted to EventArgs is polymorphism, but I think the fact that a delegate of type EventHandler<EventArgs> can be added to a EventHandler<FrobbedEventArgs> event is covariance.
nikie
A: 

Yes you can, it is type polymorphism at work. In this case you are handling a base class, as FrobbingEventArgs derives from EventArgs.

chibacity
A: 

Hi,

Since events can only be invoked from within the class that declared them, your derived class cannot directly invoke events declared within the base class.

You can achieve what you want by creating a protected invoking method for the event. By calling this invoking method, your derived class can invoke the event.

For even more flexibility, the invoking method is often declared as virtual, which allows the derived class to override it. This allows the derived class to intercept the events that the base class is invoking, possibly doing its own processing of them.

You can do:

protected void OnFrobbing(EventArgs e) 
   { 
      var handler = Frobbing; 
      if (handler != null) 
         handler(this, new e); 
   } 

Or:

protected virtual void OnFrobbing(EventArgs e) 
   { 
      var handler = Frobbing; 
      if (handler != null) 
         handler(this, new e); 
   } 
Valko
-1: It's the EventArgs that's derived, not the class raising the event...
Roger Lipscombe
+4  A: 

Maybe covariant's not the word

Close. The word you are looking for is contravariant. Conversion of a method group to a delegate type is covariant in return type and contravariant in formal parameter types.

Here's my blog article on the subject:

http://blogs.msdn.com/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx

if I have a class that raises an event, with (e.g.) FrobbingEventArgs, am I allowed to handle it with a method that takes EventArgs?

Well, you tried it and it worked, so clearly yes. For the justification in the specification see the section helpfully entitled "Method group conversions".

Eric Lippert
Eric, thanks for the correction -- changed "covariant" to "contravariant". Also thanks for the justification.
Roger Lipscombe