我试图了解事件模式决定触发事件的方法应该被声明为虚拟目的。
来自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);
}
}
}
答案 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)
)。此外,如果您考虑类似于页面模型的方式,它通常会触发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
}
按钮具有多个基类层,每个基层都相互叠加。