目录
1.事件的定义
2.事件的重要组成元素
3.事件的简略声明与完整声明,为什么会有人认为事件是特殊的委托字段?
事件:能够发什么的什么“事情”。
在C#中的角色定位:使对象/类具备通知能力的成员。
事件的作用:用于对象或类之间的动作协调或信息传递。
事件是委托字段的包装器(类比属性是字段的包装器)!用于防止委托字段的滥用,所以事件不是委托字段!事件不是委托字段!事件不是委托字段!
事件的拥有者,事件,事件的订阅,事件的响应者,事件的处理器。
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer();//timer事件的拥有者
timer.Interval = 1000;
Boy boy = new Boy();
timer.Elapsed += boy.Action;//Elapsed事件本身 boy事件的响应者 Action事件的处理器 +=事件的订阅符
Girl girl = new Girl();
timer.Elapsed += girl.Action;
timer.Start();
Console.ReadLine();
}
class Boy
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("say hello");
}
}
class Girl
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Sing");
}
}//事件第一种
}
我们来首先来看这段代码认识一下事件,首先我们来确认事件本身,+=操作符左侧的就是事件本身,即Elapsed是事件本身,该事件是System.Timers命名空间中的一个事件(C#提供的),作用是达到间隔(间隔时间)时发生。事件的拥有者是我们new出来的Timer实例,事件的响应者是我们的boy与girl实例。Action是我们事件的处理器,+=操作符就是我们的订阅。
我们可以这样理解一个事件。事件是B站UP主发布了一个视频这件事,而事件的拥有者就是B站发布视频的这个UP主,事件的订阅就是我们关注了这个B站的UP主(我们关注这个UP发布视频这件事),事件的响应者就是我们(因为我们关注了这个UP主,所以当他发布视频时我们会收到推送),事件的处理器就是我们收到视频推送后做出的反应。
static void Main(string[] args)
{
Form form = new Form();//form事件的拥有者
Controller controller = new Controller(form);//controller事件的响应者
form.ShowDialog();
}
class Controller
{
private Form form;
public Controller(Form _form)
{
if (_form != null)
{
this.form = _form;
this.form.Click += this.FormClicked;//Click事件本身,+=事件订阅符
}
}
private void FormClicked(object sender, EventArgs e)//事件处理器
{
this.form.Text = DateTime.Now.ToString();
}
}//事件第二种
我们还是先来确认事件的五个重要组成部分:事件的拥有者,事件,事件的订阅与事件的响应者和事件处理器。FormClicked就是事件的处理器,可以看到这次事件处理器(FormClicked这个方法)的参数类型与第一段代码中(Action这个方法中的参数类型不同),这是因为Click事件与Elapsed事件包装的委托不同!而委托必须与封装的方法是类型兼容的((在这里FormClicked方法是Elasped事件中包装的委托所封装的方法))!,由于包装的委托不同,他们能封装的方法也需要与委托类型兼容,所以方法的参数类型也就不同。这也就印证了开头强调了三次的,事件不是委托字段,而是委托字段的包装器。
static void Main(string[] args)
{
MyForm form = new MyForm();//form事件拥有者 form也是事件的响应者
form.Click += form.FormClicked;//Click 事件 FormClicked事件处理器 +=事件订阅符
form.ShowDialog();
}
class MyForm : Form
{
internal void FormClicked(object sender, EventArgs e)
{
this.Text = DateTime.Now.ToString();
}
}//事件第三种
一个事件的响应者可以是事件的拥有者他本身。举一个不恰当的例子,一个人他打了自己一巴掌,他自身就是事件的拥有者,且自身也是事件的响应者,事件就是打了自己一巴掌,事件的订阅符就是自身的痛觉神经对自身受到的疼痛的一个长期关注,事件的处理器就是自身被打了以后做出的反应(比如打的是脸,脸就会肿)。
事件的拥有者可以是事件的一个字段成员,下面的代码中,MyForm类中的button就是事件的拥有者,当button的Click事件发生,我们new出来的form(事件的响应者)就会根据事件处理器做出发现。
static void Main(string[] args)
{
MyForm form = new MyForm();
form.ShowDialog();
}
class MyForm : Form//Form事件响应者
{
private TextBox textBox;
private Button button;//button 事件拥有者(button也是Form的一个字段成员)
public MyForm()
{
this.textBox = new TextBox();
this.button = new Button();
this.Controls.Add(this.button);
this.Controls.Add(this.textBox);
this.button.Click += this.ButtonClicked;//click事件 +=事件订阅符
this.button.Text = "Say,Hello";
this.button.Top = 30;
}
private void ButtonClicked(object sender, EventArgs e)//事件处理器
{
this.textBox.Text = "Hello,World!!!!!!!!!!!";
}
}//事件第四种
我们来看看事件的完整声明,先确认事件的五个组成部分。
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;//order是事件 customer是事件拥有者 +=订阅符 Action处理器 waiter事件响应者
customer.CustomerAction();
customer.PayTheBill();
Console.ReadLine();
}
事件既然是委托字段的包装器,那必然要有一个委托类型的字段,我们在Customer类外部声明了一个委托类型的字段OrderEvendHandler,我们在Custorm类中声明事件,并将它包装在Order事件中(为什么说是包装呢?因为事件只能存在在+=操作符两侧(这避免了委托类型字段的滥用带来的危害,因为我们已经将委托类型字段包装在了事件中)!这与属性包装字段的概念很相似)。
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);//委托类型的声明 (不是委托类型字段的声明)
public class Customer
{
//事件的完整声明
private OrderEventHandler orderEventHandler;//委托字段
public event OrderEventHandler Order//事件声明
{
add
{
this.orderEventHandler += value;
}
remove
{
this.orderEventHandler -= value;
}
}
//事件的简略声明! 注意!它只是看上去像是在声明一个委托类型字段!但不是!
//public event OrderEventHandler Order;
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("i will pay${0}", this.Bill);
}
public void Walkin()
{
Console.WriteLine("walk into the restaurant");
}
public void SitDown()
{
Console.WriteLine("sitDonw.");
}
public void Think()
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("let me think...");
Thread.Sleep(1000);
}
if (this.orderEventHandler != null) //简略声明就没有委托类型字段了 所以得用事件本身替代(语法糖)将这里的this.orderEventHandler替换为this.Order
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "chicken";
e.Size = "large";
this.orderEventHandler.Invoke(this, e);//同上 简略声明时需要替换
}
}
public void CustomerAction()
{
Console.ReadLine();
this.Walkin();
this.SitDown();
this.Think();
}
}
为什么很多人会将事件当成特殊的委托字段呢?这是因为平常我们见到的事件的声明都是事件的简略声明 正如下面代码注释掉的简略声明一样,它看上去真的就很像是一个委托字段的声明,而C#的语法糖在后续更是加重了这一误会。由于事件是只能存在在+=操作符两侧的,而事件的简略声明又没有将其内部(隐藏起来的委托字段)的委托字段给用户使用。
左图是事件完整声明时,我们是将委托类型的字段放在了!=操作符左侧。这是合法的。
上图是事件的简略声明,可以看到,简略声明我们是拿不到其隐藏起来的委托字段的,我们在后续使用时迫不得已的将事件放在了!=左侧(事件本身是不能放在除+=操作符以外的地方的!这是不合法的!),但是由于事件的简略声明(C#的语法糖),迫不得已的只能这样使用,正是由于这一原因,极大的加深了对事件的误会。
这是我对B站Up主TimothyLiu(刘铁猛老师!!!)与BeaverJoe老师对于事件的学习总结,如有错漏,恳请指出,谢谢!