事件是什么?
事件是能夠讓對象或類具備通知能力的成員。是一種類型成員(沒有產品就沒有發布,沒有公司就沒有上市)。它是用於對象和類之間的動作協調和信息傳遞的
事件模型如下:
“發生->響應”
5個動作——
- 我有一個事件
- 我關心這個事件
- 這個事件發生了
- 關心這個事件的人被通知到
- 被通知到的人根據拿到的事件信息對事件進行響應
5個部分——
- 事件的擁有者(event source 對象)
- 事件成員(event 成員) 事件是被動觸發的,它需要通過事件擁有者進行觸發
- 事件的響應者(eventsubscriber 對象)當事件發生時,哪些對象去響應
- 事件處理器(event handler 成員)響應者的方法成員
- 事件訂閱 把事件處理器和事件關聯在一起,本質上是一種以委托類型為基礎的約定
從響應者角度來看,他們除了收到事件通知,還接收到經由事件發送過來的與事件本身相關的信息,稱為“事件參數”(Event Args)
被通知的人根據事件參數對事件進行響應(處理事件),(處理事件)所做的事情,稱為事件處理器(Event Handler)
因此我們可以說 事件的功能就是=通知+可選的事件模型
我們可以通過一個簡單的代碼例子來理解5個組成部分的關系。
查看代碼
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer();//timer是事件擁有者
timer.Interval = 1000;
Boy boy = new Boy();//boy是事件響應者
timer.Elapsed += boy.Action; //Elapsed是事件 +=是訂閱
timer.Start();
Console.ReadKey();
}
}
public class Boy
{
//事件處理器
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("hello");
}
}
我們用Boy作為事件響應者 用其事件處理器Action來訂閱事件擁有者Timer類對象中自帶的Elapsed事件
此外對於事件我們的使用情況通常有三種:
1、事件擁有者和響應者是兩個相互獨立的類,響應者類的處理器訂閱擁有者類的事件

查看代碼
namespace EventExample
{
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.Clicked;
}
}
private void Clicked(object sender, EventArgs e)
{
this.form.Text = DateTime.Now.ToString();
}
}
}
2、事件擁有者和響應者屬於同一個類,這個類自己訂閱自己的事件

namespace EventExample
{
class Program
{
static void Main(string[] args)
{
MyForm myForm = new MyForm();
myForm.Click += myForm.FormClicked;
myForm.ShowDialog();
}
}
class MyForm : Form
{
internal void FormClicked(object sender, EventArgs e)
{
this.Text = DateTime.Now.ToString();
}
}
}
3、事件擁有者作為響應者的一個成員,或者是事件響應者作為擁有者的一個成員

查看代碼
namespace EventExample
{
class Program
{
static void Main(string[] args)
{
MyForm myForm = new MyForm();
myForm.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;
this.button.Text = "Click";
this.button.Top = 50;
}
private void ButtonClicked(object sender, EventArgs e)
{
this.TextBox.Text = "Hello World!!!!!!!!!!!!";
}
}
}
上面我們用的是C#幫我們寫好的事件
那我們如何來自定義一個事件呢?C#自帶事件的內部實現是什么樣的呢?
還記得我們之前說的,事件是基於委托的嗎。這個特性會在我們聲明事件時進行一個體現
委托有以下作用:
類型兼容:委托對事件做了一個約束,規定了事件和事件處理器應當匹配
存儲方法的引用:委托類型的實例將這組匹配信息保存了下來
因此我們說事件需要搭配一個委托。它通常是聲明在事件擁有者類外的
// 步驟1,聲明delegate 如果這個委托是為了約束某個事件而聲明的委托 那么通常就將其命名為“事件名+EventHandler”
public delegate void MyEventHandler(object sender, System.EventArgs e);
我們可以給個更加具體的例子,比如我們聲明一個點餐的委托,我們想讓Customer顧客作為事件擁有者,還要傳遞價格、大小等事件參數。那我們就可以這樣聲明
public delegate void OrderEventHandler(Customer customer, EventArgs e);
之后我們就可以聲明我們的事件了。(記住事件是屬於事件擁有者的,它需要寫在事件擁有者類的內部)
有完整聲明和簡略聲明兩種形式
完整聲明:
private OrderEventHandler OrderEventHandler;//聲明委托類型字段。用於存儲和引用事件處理器
public event OrderEventHandler Order//聲明事件Order。用OrderEventHandler來約束事件
{
add//事件處理器的添加器
{
this.OrderEventHandler += value;
}
remove//事件處理器的移除器
{
this.OrderEventHandler -= value;
}
}
簡略聲明:
private event MyEventHandler myevent;
多種事件訂閱格式
this.button3.Click += MyButton_Click;//第一種掛接事件方法
this.button3.Click += new EventHandler(this.MyButton_Click);//第二種
this.button3.Click += delegate (object sender, EventArgs e)//第三種 匿名方法
{
this.MyTextBox.Text = "haha";
};
this.button3.Click += (sender,e) => //第四種 lambda表達式
{
this.MyTextBox.Text = "hoho";
};
事件與委托的關系
事件真的是“以特殊方式聲明的委托字段/實例嗎”?
不是!只是聲明的時候“看起來像”(對比委托字段的和事件的簡化聲明,field-like)
事件聲明的時候使用了委托類型,簡化聲明造成事件看上去像一個委托的字段(實例),而event關鍵字則更像是一個修飾符——這就是錯覺的來源之一
訂閱事件的時候+=操作符后面可以是一個委托實例,這與委托實例的賦值方法語法相同,這也讓事件看起來像是一個委托字段——這是錯覺的又一來源
重申:事件的本質是加裝在委托字段上的一個“蒙板”(mask),是個起掩蔽作用的包裝器,這個用於阻擋非法操作的“蒙板”絕不是委托字段本身
為什么要使用委托類型來聲明事件?
站在source的角度來看,是為了表明source能對外傳遞哪些消息
站在subscriber的角度來看,它是一種約定,是為了約束能夠使用什么樣簽名的方法來處理(響應)事件
委托類型的實例將用於存儲(引用)事件處理器
對比事件和屬性
屬性不是字段——很多時候屬性是字段的包裝器,這個包裝器用來保護字段不被濫用
事件不是委托字段——它是委托字段的包裝器,這個包裝器用於保護委托字段不被濫用
包裝器永遠都不可能是被包裝的東西
下面給出一個參考實例
查看代碼
namespace EventExample
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;//掛接事件。waiter的Action訂閱着customer的Order
customer.Action();
customer.PayTheBill();
}
}
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);//聲明一個委托類型,專門用來聲明事件,約束事件處理器
public class OrderEventArgs : EventArgs//聲明用來傳遞消息的類,派生自EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
public class Customer //事件發起者
{
public event OrderEventHandler Order;//聲明事件Order。用OrderEventHandler來約束事件
public double Bill { get; set; }
public void Walkin()
{
Console.WriteLine("Walk in 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 = "Kongpao Chicken";
e.Size = "large";
this.Order.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
Walkin();
Sitdown();
Think();
}
public void PayTheBill()
{
Console.WriteLine("I Will pay ${0}.",this.Bill);
}
}
public class Waiter //事件的響應者Waiter
{
public 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;
}
}
}
