今天记录一个小例子,如何使用事件,
代码的逻辑是设置一个闹钟,每隔1秒打印两个字段。
需要引用 using System.Timers;
一个类中最重要的就是属性,事件和方法。
属性就是当前这个类处于什么状态,
方法表示这个类能做什么,
事件表示这个类在什么情况下通知谁。
类中的功能概括的说就是,存储数据做事情,通知别人。
事件的拥有者,事件的source和事件消息的发送者是一回事,这就是为什么这个参数叫做sender,sender的意思就是事件消息的发送者。
对比事件与属性
属性不是字段----很多时候属性是字段的包装器,这个包装器用来保护字段不被滥用
事件不是委托字段----它是委托字段的包装器,这个包装器用来保护委托字段不被滥用
包装器永远都不可能被包装的东西。
事件模式的五个组成部分
- 事件的拥有者(event source ,对象)
- 事件成员(event,成员)
- 事件的响应者(event subscriber,对象)
- 事件处理器(event handler,成员)—— 本质是一个回调方法
- 事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”
注意
- 事件处理器是方法成员
- 挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个“语法糖”
- 事件处理对事件的订阅也不是随意的,匹配与否由声明事件时所使用的委托类型来检测
- 事件可以同步也可以异步调用
事件拥有者和事件响应者模型
图1
图2:事件的拥有者时事件的响应者的一个字段成员
图3:
图4:事件的拥有者同时也是事件的响应者
using System;
using System.Timers;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer();
timer.Interval = 1000;
Boy boy = new Boy();
Grid grid = new Grid();
timer.Elapsed += boy.Action;
timer.Elapsed += grid.Action;
timer.Start();
Console.ReadLine();
}
}
class Boy
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Jump");
}
}
class Grid
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("singing");
}
}
}
代码解释:
Timer timer = new Timer();
timer.Interval = 1000;
这两行代码是定义一个定时器,第一行是创建一个定时器,第二行代码是设置定时器的时间,1000是1000毫秒,也就是1秒。
Boy boy = new Boy();
Grid grid = new Grid();
timer.Elapsed += boy.Action;
timer.Elapsed += grid.Action;
这对代码第一行和第二行就是步解释了,
第三行是timer订阅boy这个事件,boy.Action是事件响应者。第四行相似。
timer.Start();
Console.ReadLine();
这段代码就是启动定时器。
分析这段代码
事件拥有者 Timer timer = new Timer();
事件 Elapsed
事件响应者 boy的对象
事件处理器是 boy的Action的方法。
事件订阅 timer.Elapsed += boy.Action;
事件订阅的操作符是 += +=左边是事件,右边是事件处理器。
图1的代码demo
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Form form = new Form();
Controller controller = new Controller(form);
form.ShowDialog();
}
}
class Controller
{
private Form form;
public Controller(Form form)
{
if (form != null)
{
this.form = form;
this.form.Click += this.FormClicked;
}
}
private void FormClicked(object sender, EventArgs e)
{
this.form.Text = DateTime.Now.ToString();
}
}
}
分析代码:
事件拥有者 Form form = new Form();
事件 是form的click
事件的响应者 Controller controller = new Controller(form);
事件处理器 FormClicked
事件的订阅 this.form.Click += this.FormClicked;
图4代码示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.Click += form.FormClicked;
form.ShowDialog();
}
}
class MyForm : Form
{
internal void FormClicked(object sender, EventArgs e)
{
this.Text = DateTime.Now.ToString();
}
}
}
分析代码:
事件的拥有者 MyForm form = new MyForm();
事件 Click事件
事件的响应者 MyForm form = new MyForm();
事件的处理器 FormClicked
事件的订阅 form.Click += form.FormClicked;
图2/3 代码示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.ShowDialog();
}
}
class MyForm : Form
{
private TextBox textBox;
private Button button;
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;
}
private void ButtonClicked(object sender, EventArgs e)
{
this.textBox.Text = "Hello world!!!!!!!!!!!!!!!!!";
}
}
}
分析代码:
事件拥有者 button
事件 click
事件响应者 MyForm对象
事件处理器 ButtonClicked
事件订阅 this.button.Click += this.ButtonClicked;
sender的使用
button1和button2同时绑定在一个事件上 buttonclick
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonclick(object sender, EventArgs e)
{
if (sender == this.button1)
{
this.textBox1.Text = "Hello";
}
if (sender == this.button2)
{
this.textBox1.Text = "world";
}
}
}
}
事件比较古老的写法:这两种写法都一样。
this.button3.Click += this.buttonclick;
this.button3.Click += new EventHandler(this.buttonclick);
事件的匿名方法使用:
this.button3.Click += delegate (object sender, EventArgs e)
{
this.textBox1.Text = "haha";
};
取代匿名方法的方法lamda表达式:这种写法比较流行
this.button3.Click += (object sender, EventArgs e) => {
this.textBox1.Text = "hoho";
};
这样写也可以:去表object和EventArgs
this.button3.Click += ( sender, e) => {
this.textBox1.Text = "hoho";
自定义事件
事件的声明:
完整声明
简略声明(字段是声明,field-like)
有了委托字段/属性,为什么还需要事件?
为了程序的逻辑更加“有道理”、更加安全,谨防“借刀杀人”
所以事件的本质是委托字段的一个包装器
- 这个包装器对委托字段的访问起限制作用,相当于一个“蒙版”
- 封装(encapsulation)的一个重要功能就是隐藏
- 事件对外界的隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能
- 添加/移除事件处理器的时候可以直接使用方法名,这是委托实例所不具备的功能
用于声明事件的委托类型的命名约定
- 用于声明Foo时间的额委托,一般命名为FooEventHandler(除非是一个非常通用的事件约束)
- FooEventhandler委托的参数一般有两个(由Win32 API演化而来,历史悠久)
- 第一个是object类型,名字为sender,实际上就是事件的拥有者、事件的source
- 第二个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e,也就是前面讲过的事件参数
- 虽然没有官方的说法,但我们可以把委托的参数列表看做是事件发生后发送给事件响应者的“事件消息“
- 触发Foo事件的方法一般命名weiOnFoo,即”因何引发“、”事出有因“
- 访问级别为protected,不能为public,不然又可以成了可以”借刀杀人“了
事件的命名约定
- 带有时态的动词或动词短语
- 事件拥有者”正在做“什么事情,用进行时;事件拥有者”做完了“什么事情,用完成时
完整事件的声明
using System;
using System.Threading;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs : EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
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 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("sit down");
}
public void Think()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Let me think....");
Thread.Sleep(1000);
}
if (this.orderEventHandler != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "kongbaojiding";
e.Size = "large";
this.orderEventHandler.Invoke(this,e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
internal void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("I will serve you the dish -{0}",e.DishName);
double price = 10;
switch (e.Size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}
简化事件声明
其实就是把这段代码注释了换成以public event OrderEventHandler Order;再把orderEventHandler换成了Order
//private OrderEventHandler orderEventHandler;
//public event OrderEventHandler Order
//{
// add
// {
// this.orderEventHandler += value;
// }
// remove
// {
// this.orderEventHandler -= value;
// }
//}
using System;
using System.Threading;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs : EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
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("sit down");
}
public void Think()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Let me think....");
Thread.Sleep(1000);
}
if (this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "kongbaojiding";
e.Size = "large";
this.Order.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
internal void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("I will serve you the dish -{0}", e.DishName);
double price = 10;
switch (e.Size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}