事件
一、事件初印象
首先看个播放器的案例
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MP3 m = new MP3(); 6 7 //启动前 8 m.BeforeStartMP3 = () => { Console.WriteLine("初始化歌词!"); }; 9 10 m.BeforeStopMP3 = () => { Console.WriteLine("保存当前进度!"); }; 11 12 m.Start(); 13 14 m.TurnOffPower(); 15 16 Console.ReadKey(); 17 } 18 } 19 20 public class MP3 21 { 22 23 //在启动或者关闭mp3之前,每个人想进行的操作可能不同,这里用委托实现用户的自定义操作 24 public Action BeforeStartMP3; 25 public Action BeforeStopMP3; 26 27 /// <summary> 28 /// 播放音乐 29 /// </summary> 30 public void PlayMusic() 31 { 32 Console.WriteLine("音乐播放中……"); 33 } 34 35 36 /// <summary> 37 /// 启动mp3 38 /// </summary> 39 public void Start() 40 { 41 if (BeforeStartMP3!=null) 42 { 43 //在启动播放的时候,触发了这个事件 44 BeforeStartMP3(); 45 } 46 47 PlayMusic(); 48 } 49 50 public void TurnOffPower() 51 { 52 if (BeforeStopMP3 != null) 53 { 54 //在停止播放之前触发了这个事件 55 BeforeStopMP3(); 56 } 57 58 Stop(); 59 60 } 61 62 /// <summary> 63 /// 关闭mp3 64 /// </summary> 65 public void Stop() 66 { 67 Console.WriteLine("停止播放音乐……"); 68 } 69 70 71 }
上面的例子虽然是用委托实现的,但是达到了使用事件的效果。
二、事件进阶
先用委托来模拟实现三连击的效果
用户自定义控件中的代码:
1 namespace 事件进阶 2 { 3 public partial class UCTripleButton : UserControl 4 { 5 public UCTripleButton() 6 { 7 InitializeComponent(); 8 } 9 10 public Action TripleClick; 11 12 int count = 0; 13 private void btnTripleClick_Click(object sender, EventArgs e) 14 { 15 count++; 16 if (count>=3) 17 { 18 //MessageBox.Show("点击了三次!"); 19 if (TripleClick!=null) 20 { 21 TripleClick(); 22 } 23 count = 0; 24 } 25 } 26 } 27 }
Form1中的代码:
1 public partial class Form1 : Form 2 { 3 public Form1() 4 { 5 InitializeComponent(); 6 } 7 8 private void Form1_Load(object sender, EventArgs e) 9 { 10 //将方法传给委托变量 11 ucTripleButton1.TripleClick = Form1TripleClick; 12 } 13 14 private void Form1TripleClick() 15 { 16 MessageBox.Show("这是Form1"); 17 } 18 19 //在用户控件外面调用,点击一次就能弹出三连击的效果 20 private void button1_Click(object sender, EventArgs e) 21 { 22 if (ucTripleButton1.TripleClick!=null) 23 { 24 ucTripleButton1.TripleClick(); 25 } 26 } 27 28 //委托变量是public,可以在外面调用 29 private void button2_Click(object sender, EventArgs e) 30 { 31 ucTripleButton1.TripleClick=null; 32 } 33 }
Form2中的代码:
1 namespace 事件进阶 2 { 3 public partial class Form2 : Form 4 { 5 public Form2() 6 { 7 InitializeComponent(); 8 } 9 10 private void Form2_Load(object sender, EventArgs e) 11 { 12 ucTripleButton1.TripleClick = Form2TripleClick; 13 } 14 15 private void Form2TripleClick() 16 { 17 MessageBox.Show("这是Form2"); 18 } 19 } 20 }
这样能实现三连击事件的效果,但是我们在Form1窗口中增加其他按钮,由于委托是public,button2可以直接调用绑定的方法,button3可以清除委托绑定的方法,这样调用委托的权限容易被改变(在类的外部随意调用),如同别人可以操作你的银行卡,很显然,不合理。于是就有了事件。
事件只能在类的内部被触发,但是可以在外部绑定或者取消。
编写事件的代码:
1 public partial class UCTripleButton : UserControl 2 { 3 public UCTripleButton() 4 { 5 InitializeComponent(); 6 } 7 8 9 //Action前面加上event关键字,就变成了事件 10 public event Action TripleClick; 11 12 int count = 0; 13 private void btnTripleClick_Click(object sender, EventArgs e) 14 { 15 count++; 16 if (count>=3) 17 { 18 //MessageBox.Show("点击了三次!"); 19 if (TripleClick!=null) 20 { 21 TripleClick(); 22 } 23 count = 0; 24 } 25 } 26 }
Form1触发事件:
1 namespace 事件进阶 2 { 3 public partial class Form1 : Form 4 { 5 public Form1() 6 { 7 InitializeComponent(); 8 } 9 10 private void Form1_Load(object sender, EventArgs e) 11 { 12 //将方法传给委托变量 13 //ucTripleButton1.TripleClick = Form1TripleClick; 14 15 //事件只能用+=,-=绑定,避免被随意调用和赋值的时候被全部清空 16 ucTripleButton1.TripleClick += UcTripleButton1_TripleClick; 17 } 18 19 private void UcTripleButton1_TripleClick() 20 { 21 MessageBox.Show("这是Form1"); 22 } 23 24 private void Form1TripleClick() 25 { 26 MessageBox.Show("这是Form1"); 27 } 28 29 //在用户控件外面调用,点击一次就能弹出三连击的效果 30 private void button1_Click(object sender, EventArgs e) 31 { 32 //if (ucTripleButton1.TripleClick!=null) 33 //{ 34 // ucTripleButton1.TripleClick(); 35 //} 36 37 ucTripleButton1.TripleClick += UcTripleButton1_TripleClick; 38 } 39 40 //委托变量是public,可以在外面调用 41 private void button2_Click(object sender, EventArgs e) 42 { 43 //ucTripleButton1.TripleClick=null; 44 ucTripleButton1.TripleClick += null; 45 } 46 } 47 }
三、补充
回到第一个案例,我们用委托实现了播放器在播放歌曲的时候实现了加载歌词,保存进度的功能 ,但是由于委托是public的,所以也可以直接调用加载歌词和保存进度的方法,根本不需要启动和关闭播放器,就能直接实现这个效果,很显然这不合理。这里可以用事件改造。
四、事件和委托的区别
委托是一种数据类型,事件是一个对象,它是基于委托实现的,对委托进行了封装。二者没有可比性。