Why events can't be used in the same way in derived classes as in the base class in C#?

前端 未结 6 1285
庸人自扰
庸人自扰 2020-11-29 07:27

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
{
    pub         


        
相关标签:
6条回答
  • 2020-11-29 07:44

    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.

    0 讨论(0)
  • 2020-11-29 07:51

    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
    }
    
    0 讨论(0)
  • 2020-11-29 07:53

    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.

    0 讨论(0)
  • 2020-11-29 07:58

    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.

    0 讨论(0)
  • 2020-11-29 07:58

    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.

    0 讨论(0)
  • 2020-11-29 08:03

    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.

    0 讨论(0)
提交回复
热议问题