Delegate和Event的区别(学习)

委托

 定义委托的语法和定义方法比较相似,只是比方法多了一个关键字delegate。 我们都知道方法就是将类型参数化,传输某种类型的参数到方法中,而委托是将方法参数化,就是将方法作为一个参数传到一个委托中。

 

1: 委托的声明

public     delegate      void     MyDelegate    ();  

访问修饰符    delegate    返回类型      委托名      参数列表  

 

  • 委托传入方法,方法传入变量
  • 委托的返回值可以是任何类型,比如:string,或者int
  • 可以放若干参数作为输入
  • 注意参数列表的形参要和实参一致

 委托只要定义就可以了,不关心其功能如何实现,具体功能实现由注册的方法完成。不像方法定义的时候内部要写具体的功能实现

 

 2: 委托的使用

注册委托有两种方法:

第一种委托名=方法名(直接赋值)    或者     委托名+= 方法名(+=赋值)

第二种委托本质也是一个类,只是一个特殊的类,所以我们也可以实例化一个委托对象通过委托构造函数来注册要委托的方法:委托名 实例名= new 委托名(方法名) 

 

实例: 

 

界面上就是简单的四个按钮 两个属于委托,两个属于事件,都是一个用来执行,一个用来干扰,以便于来理解委托事件

然后看后台代码,首先我定义了一个Test类,声明委托,实例了委托,还声明了事件,写了个方法用来触发事件,代码如下:

 

View Code
 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类,代码如下:

 

View Code
 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挂载到委托链上,代码如下:

 

View Code
 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事件,代码如下:

 

View Code
1 //执行委托
2 2         private void button1_Click(object sender, EventArgs e)
3 3         {
4 4             //执行委托链中的方法
5 5             test.myDel();
6 6         }

显示效果:

  

说明没问题,方法都挂载到委托链上了,但是如果这时候我们来进行下干扰呢?各位想想看,结果会是怎么样了呢,先贴上干扰的代码:
View Code
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       
其实这里的所谓的“干扰”,就是给以存在的委托链中附加了一个 null 的对象,然后再给委托附加C方法,此时按如下步骤运行程序:

 

看看结果会怎么样,哎,发现就只有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可执行文件,看看当我们给事件注册上方法时,事件内部在做什么,附上反编译代码:

 

 

从 反编译中就可以充分看出,事件的本质就是一个委托,它的内部仍然是用一个委托,该委托的名称的方法就只有和事件名首字母的大小写不同而已,该委托来负责接收事件,然后通过addremove方法来实现委托的注册和删除,即当我们给委托注册一个方法时,内部就调用add方法,通过 Delegate.Combine()方法来实现将方法附加到委托链上,当我们删除委托链上的一个方法时,即内部调用remove方法,通过 Delegate.Remove()方法,将该方法从委托链上移除,所以通过反编译工具,我们就可以清楚的知道事件内部的实现代码了。

接下来看看事件的效果,首先在Page_Load事件中注册事件,我们将A,B方法注册到事件上,代码如下:

View Code
 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         }
然后我们点击[执行事件]按钮看看效果,如图:

 

和刚开始的委托一样,没有问题,两个方法都触发了,接下来再让我们来实施干扰,附上干扰代码:
View Code
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         }

 

运行如图:

 

运行结果如下:

 

所以没有问题,即使你附加了一个null对象,三个方法照样依次执行了,这就是事件的封装效果了.......
一句话:用事件注册的方法不可以=赋值,需要用+=/-=来注册删除方法或者特殊的事件的方法clean()来清空
 
 
 
代码执行结果
 
 
代码都写在MainWindow.xaml.cs
代码如下:
View Code
 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 }

 


 

 

posted @ 2012-08-30 12:22  若愚Shawn  阅读(486)  评论(0编辑  收藏  举报