views:

800

answers:

6

In following code, I want to extend the behaviour of a class by deriving/subclassing it, and make use of an event of the base class:

public class A
{
    public event EventHandler SomeEvent;

    public void someMethod()
    {
        if(SomeEvent != null) SomeEvent(this, someArgs);
    }
}

public class B : A
{
    public void someOtherMethod()
    {
        if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible?
//Error: The event 'SomeEvent' can only appear on the left hand side of += or -= 
//(except when used from within the type 'A')
    }
}

Why isn't it possible?

And what is the common solution for this kind of situation?

+8  A: 

The standard practice here is to have a protected virtual method OnSomeEvent on your base class, then call that method in derived classes. Also, for threading reasons you will want to keep a reference to the handler before checking null and calling it.

For an explanation of the why read Jon Skeet's answer or the C# specification which describes how the compiler automatically creates a private field.

Here is one possible work around.

public class A
{
    public event EventHandler SomeEvent;

    public void someMethod()
    {
        OnSomeEvent();
    }

    protected void OnSomeEvent()
    {
        EventHandler handler = SomeEvent;
        if(handler != null)
            handler(this, someArgs);
    }
}

public class B : A
{
    public void someOtherMethod()
    {
        OnSomeEvent();
    }
}

Edit: Updated code based upon Framework Design Guidelines section 5.4 and reminders by others.

Todd White
Don't use the prefix `Raise...`, it should always be `On...`. Also the base class's method should be virtual.
Keith
Thanks also for the hint for multithreading.
mr_georg
A: 

Wrap it with a protected virtual On... method:

public class BaseClass
{
    public event EventHandler<MyArgs> SomeEvent;

    protected virtual void OnSomeEvent()
    {
        if(SomeEvent!= null)
            SomeEvent(this, new MyArgs(...) );
    }
}

Then override this in a derived class

public class DerivedClass : BaseClass
{
    protected override void OnSomeEvent()
    {
        //do something

        base.OnSomeEvent();
    }
}

You'll set this pattern all over .Net - all form and web controls follow it.

Do not use the prefix Raise... - this is not consistent with MS's standards and can cause confusion elsewhere.

Keith
+8  A: 

Others have explained how to get round the issue, but not why it's coming up.

When you declare a public field-like event, the compiler creates a public event, and a private field. Within the same class (or nested classes) you can get at the field directly, e.g. to invoke all the handlers. From other classes, you only see the event, which only allows subscription and unsubscription.

Jon Skeet
Very interesting. do you have a link to the relevant documentation?
biozinc
+1, nice detail on the intricacies.
John Rudy
Not documentation as such, but http://pobox.com/~skeet/csharp/events.html contains more about events and delegates in general.
Jon Skeet
+3  A: 

Todd's answer is correct. Often you will see this implemented throughout the .NET framework as OnXXX(EventArgs) methods:

public class Foo
{
    public event EventHandler Click;

    protected virtual void OnClick(EventArgs e)
    {
        var click = Click;
        if (click != null)
            click(this, e);
    }
}

I strongly encourage you to consider the EventArgs<T>/EventHandler<T> pattern before you find yourself making all manner of CustomEventArgs/CustomEventHandler for raising events.

cfeduke
+1  A: 

The reason the original code doesn't work is because you need to have access to the event's delegate in order to raise it, and C# keeps this delegate private.

Events in C# are represented publicly by a pair of methods, add_SomeEvent and remove_SomeEvent, which is why you can subscribe to an event from outside the class, but not raise it.

Tim Robinson
A: 

My answer would be that you shouldn't have to do this.

C# nicely enforces Only the type declaring/publishing the event should fire/raise it. If the base class trusted derivations to have the capability to raise its events, the creator would expose protected methods to do that. If they don't exist, its a good hint that you probably shouldn't do this.

My contrived example as to how different the world would be if derived types were allowed to raise events in their ancestors. Note: this is not valid C# code.. (yet..)

public class GoodVigilante
{
  public event EventHandler LaunchMissiles;

  public void Evaluate()
  {
    Action a = DetermineCourseOfAction(); // method that evaluates every possible
// non-violent solution before resorting to 'Unleashing the fury'

    if (null != a) 
    { a.Do(); }
    else
    {  if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); }
  }

  virtual protected string WhatsTheTime()
  {  return DateTime.Now.ToString();  }
  ....   
}
public class TriggerHappy : GoodVigilante
{
  protected override string WhatsTheTime()
  {
    if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty);
  }

}

// client code
GoodVigilante a = new GoodVigilante();
a.LaunchMissiles += new EventHandler(FireAway);
GoodVigilante b = new TriggerHappy();             // rogue/imposter
b.LaunchMissiles += new EventHandler(FireAway);

private void FireAway(object sender, EventArgs e)
{
  // nuke 'em
}
Gishu