为什么事件模式将事件firerer方法声明为虚拟?

时间:2018-06-14 07:51:15

标签: c# events

我试图了解事件模式决定触发事件的方法应该被声明为虚拟目的。

来自C#6 in a Nutshell, from Joseph and Ben Albahari, O'Reilley

  

最后,该模式要求您编写一个触发的受保护虚拟方法   事件。该名称必须与事件名称匹配,前缀为单词On,和   然后接受一个EventArgs参数:

我创建的片段下面是为了尝试调查。 我的印象是,这个想法是允许继承类完全覆盖事件的处理方式,开箱即用(原始类)。但下面的代码段显示这是不可能的,因为派生类永远不能调用事件对象本身(通过关键字事件对代理强加的约束的目标之一)。该事件只能在包含类中调用。

现在,由于模式还要求触发事件的方法只是检查事件是否为空,然后调用委托,无论每个用户要求做什么,通过使用该方法还剩下什么那个事件是虚拟的吗?继承类必须按照它在广播类中的方式调用事件,因此剩下的就是添加功能。但这正是他们通过订阅事件可以实现的,换句话说,通过在事件被触发时添加对外部函数的调用。

我希望我的措辞足够清楚。

namespace eventsPatternVirtualEventFirerer
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var obj = new X();

            obj.ev += Obj_ev;
            obj.Start();
        }

        private static void Obj_ev(object sender, EventArgs e)
        {
            Console.WriteLine("subscriber code...");
        }
    }

    public class X
    {
        public event EventHandler<EventArgs> ev;

        protected virtual void OnEvent(EventArgs e)
        {
            Console.WriteLine("original implementation...");
            ev?.Invoke(this, e);
        }

        public void Start()
        {
            OnEvent(EventArgs.Empty);
        }
    }

    public class X2 : X
    {
        public X2()
        {
        }

        protected override void OnEvent(EventArgs e)
        {
            Console.WriteLine("inheriting class implementation overwrite...");
            //compilation error - "the event 'X.ev' can only appear on the left hand side of += or -= (except when used from within the type 'X')"
            ev?.Invoke(this, e);
        }
    }
}

2 个答案:

答案 0 :(得分:3)

我认为目的是允许派生类在事件被触发之前/之后做某事

public class X2 : X
{
    public X2()
    {
    }

    protected override void OnEvent(EventArgs e)
    {
        // Do something before the event
        base.OnEvent(e);
        // Do something after the event
    }
}

您可以在派生类中添加/更改一些内容

  • 添加OnBeforeEvent / OnAfterEvent添加。
  • 选择不广播事件(有条件不要调用base.OnEvent(e))。
  • 以某种方式改变事件arg。

此外,如果您考虑类似于页面模型的方式,它通常会触发Load事件以在页面加载时通知。如果没有protected OnLoad方法,派生类必须订阅它自己的Load事件才能对加载执行某些操作

public class MyPage : Page
{
    protected override void OnLoad(EventArgs e)
    {
        // do something when the page is loaded
         base.OnLoad(e);
    }

}

public class MyPage : Page
{
    public MyPage() : base()
    {
         this.Load += (sender,e) => {
             // bleugh - subscribing to my own events 
         }
    }
}

答案 1 :(得分:1)

一个很好的例子可能是Windows窗体中的Paint事件。

// in MyButton : BaseButton : Control

void override OnPaint(object s, PaintEveargs e)
{
   base.OnPaint(s, e); // Control: Draw background, BaseButton: draw border
   // draw my own stuff
}

按钮具有多个基类层,每个基层都相互叠加。