Delegate和Event的区别(学习)
委托
定义委托的语法和定义方法比较相似,只是比方法多了一个关键字delegate。 我们都知道方法就是将类型参数化,传输某种类型的参数到方法中,而委托是将方法参数化,就是将方法作为一个参数传到一个委托中。
1: 委托的声明:
public delegate void MyDelegate ();
访问修饰符 delegate 返回类型 委托名 参数列表
- 委托传入方法,方法传入变量
- 委托的返回值可以是任何类型,比如:string,或者int
- 可以放若干参数作为输入
- 注意参数列表的形参要和实参一致
* 委托只要定义就可以了,不关心其功能如何实现,具体功能实现由注册的方法完成。不像方法定义的时候内部要写具体的功能实现
2: 委托的使用:
注册委托有两种方法:
第一种:委托名=方法名(直接赋值) 或者 委托名+= 方法名(+=赋值)
第二种:委托本质也是一个类,只是一个特殊的类,所以我们也可以实例化一个委托对象通过委托构造函数来注册要委托的方法:委托名 实例名= new 委托名(方法名)
实例:
界面上就是简单的四个按钮 两个属于委托,两个属于事件,都是一个用来执行,一个用来干扰,以便于来理解委托事件
然后看后台代码,首先我定义了一个Test类,声明委托,实例了委托,还声明了事件,写了个方法用来触发事件,代码如下:
1 public class Test 2 2 { 3 3 //声明一个委托 4 4 public delegate void MyDelegate(); 5 5 6 6 //创建一个委托实例 7 7 public MyDelegate myDel; 8 8 //声明一个事件 9 9 public event MyDelegate EventMyDel; 10 10 11 11 //事件触发机制(必须和事件在同一个类中) 外界无法直接用EventMyDel()来触发事件 12 12 public void DoEventMyDel() 13 13 { 14 14 EventMyDel(); 15 15 } 16 16 }
然后为了方便测试,我还定义了三个方法在Form1类,代码如下:
1 1 //方法A 2 2 public void Fun_A() 3 3 { 4 4 MessageBox.Show("A 方法触发了"); 5 5 } 6 6 7 7 //方法B 8 8 public void Fun_B() 9 9 { 10 10 MessageBox.Show("B 方法触发了"); 11 11 } 12 12 //方法C 13 13 public void Fun_C() 14 14 { 15 15 MessageBox.Show("C 方法触发了"); 16 16 }
然后在页面Page_Load时注册委托,将方法A和方法B挂载到委托链上,代码如下:
1 //页面载入事件 2 2 private void Form1_Load(object sender, EventArgs e) 3 3 { 4 4 5 5 //注册委托(挂载方法) 6 6 test.myDel += Fun_A; 7 7 test.myDel += Fun_B; 8 8 9 9 //注册事件(挂载方法) 10 10 //test.EventMyDel += Fun_A; 11 11 //test.EventMyDel += Fun_B; 12 12 }
然后给[执行委托]按钮添加Click事件,代码如下:
1 //执行委托 2 2 private void button1_Click(object sender, EventArgs e) 3 3 { 4 4 //执行委托链中的方法 5 5 test.myDel(); 6 6 }
显示效果:
1 1 //委托干扰事件 2 2 private void button2_Click(object sender, EventArgs e) 3 3 { 4 4 //给委托赋 null 值 5 5 test.myDel = null; 6 6 7 7 //给委托挂载上C方法 8 8 test.myDel += Fun_C; 9 9
看看结果会怎么样,哎,发现就只有C方法被触发了:
大家想想,为什么会这样呢??为什么A,B方法不执行了呢??这是因为原来的委托链 是已经挂载了A,B两个方法了,但这时突然被干扰了下,又附加了一个null对 象,破坏了原有的委托链了,但这又说明了什么呢?因为委托本质就是一个类, 它包含一对有用的字段,第一个字段是存放该对象的引用,第二个字段存放一个方法的指针,所以我们可以把委托理解成一个指向函数的指针,当一个方法作为参数 赋值给一个委托的时候,该委托就指向了该方法的首地址,即方法名,所以当我们给委托注册A,B两个方法,该委托就同时指向了A,B两个方法的首地址,但这 是又突然给委托赋值了,且赋值了一个null对象,注意这里用的是赋值符号[=],这就是说让该委托清除原有的指针指向,此时指向一个null,之后又给 委托注册了C方法,所以此时委托即指向null,又指向了C方法的首地址,这就是为什么运行时只会看到C方法被触发的原因了!
那就是说现在的委托变得不安全了,哪天一个项目中给委托注册了很多方法,但突然被干扰了下,前面的注册都失效了,那我们前面做的工作不是白做了,那有没有办法可以防止这种干扰呢??答案是当然有,相信聪明的你也应该猜到了,这时就是事件该上场的时候了。(一句话:委托可以用“=”赋值,赋值"null"清空已注册的方法)
事件
事件就是一个特殊的委托,委托和事件就类似于private variable(小写)和其对应的public property(大写)的封装关系,事件是对委托做了一个封装
先看看声明一个事件:
public event MyDelegate EventMyDel;
public event 委托名 事件名称
接下来让我们来看看我们用反编译工具来反编译下该项目的exe可执行文件,看看当我们给事件注册上方法时,事件内部在做什么,附上反编译代码:
从 反编译中就可以充分看出,事件的本质就是一个委托,它的内部仍然是用一个委托,该委托的名称的方法就只有和事件名首字母的大小写不同而已,该委托来负责接收事件,然后通过add,remove方法来实现委托的注册和删除,即当我们给委托注册一个方法时,内部就调用add方法,通过 Delegate.Combine()方法来实现将方法附加到委托链上,当我们删除委托链上的一个方法时,即内部调用remove方法,通过 Delegate.Remove()方法,将该方法从委托链上移除,所以通过反编译工具,我们就可以清楚的知道事件内部的实现代码了。
接下来看看事件的效果,首先在Page_Load事件中注册事件,我们将A,B方法注册到事件上,代码如下:
1 public delegate void MyDelegate(); 2 public MyDelegate myDel; 3 public event MyDelegate EventMyDel; 4 public void DoEventMyDel() 5 { 6 EventMyDel(); 7 } 8 9 public MainWindow() 10 { 11 InitializeComponent(); 12 myDel += Fun_A; 13 myDel += Fun_B; 14 EventMyDel += Fun_A; 15 EventMyDel += Fun_B; 16 }
1 //干扰事件 2 2 private void button4_Click(object sender, EventArgs e) 3 3 { 4 4 //注册干扰事件 5 5 test.EventMyDel = null; 6 6
说明了什么?说明我们没法对事件用赋值[=]号来进行注册,这就避免了破坏直接委托链的指针指向了。但你会想了,那如果我们用附加[+=]上一个null对象呢?会干扰到么?好的,我们实施下,修改代码,如下:
1 复制代码 2 1 //干扰事件 3 2 private void button4_Click(object sender, EventArgs e) 4 3 { 5 4 //注册干扰事件 6 5 test.EventMyDel += null; 7 6 8 7 //注册C方法 9 8 test.EventMyDel += Fun_C; 10 9 11 10 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Windows; 6 using System.Windows.Controls; 7 using System.Windows.Data; 8 using System.Windows.Documents; 9 using System.Windows.Input; 10 using System.Windows.Media; 11 using System.Windows.Media.Imaging; 12 using System.Windows.Navigation; 13 using System.Windows.Shapes; 14 15 namespace TryDelegate 16 { 17 /// <summary> 18 /// Interaction logic for MainWindow.xaml 19 /// </summary> 20 public partial class MainWindow : Window 21 { 22 public delegate void MyDelegate(); 23 public MyDelegate myDel; 24 public event MyDelegate EventMyDel; 25 public void DoEventMyDel() 26 { 27 EventMyDel(); 28 } 29 30 public MainWindow() 31 { 32 InitializeComponent(); 33 myDel += Fun_A; 34 myDel += Fun_B; 35 EventMyDel += Fun_A; 36 EventMyDel += Fun_B; 37 } 38 39 public void Fun_A() 40 { 41 MessageBox.Show("A 方法触发了"); 42 } 43 public void Fun_B() 44 { 45 MessageBox.Show("B 方法触发了"); 46 } 47 public void Fun_C() 48 { 49 MessageBox.Show("C 方法触发了"); 50 } 51 52 private void Button_Click(object sender, RoutedEventArgs e) 53 { 54 myDel(); 55 } 56 57 private void Button_Click_1(object sender, RoutedEventArgs e) 58 { 59 myDel = null; 60 myDel += Fun_C; 61 } 62 63 private void Button_Click_2(object sender, RoutedEventArgs e) 64 { 65 DoEventMyDel(); 66 } 67 68 private void Button_Click_3(object sender, RoutedEventArgs e) 69 { 70 EventMyDel += null; 71 EventMyDel += Fun_C; 72 } 73 74 } 75 }