.NET 中的 委托与事件

 
一、什么是委托

      下面引用自 MSDN

      委托类型声明的格式如下:

public   delegate   void  TestDelegate( string  message);


      delegate 关键字用于声明一个引用类型,该引用类型可用于封装命名方法或匿名方法。委托类似于 C++ 中的函数指针;但是,与函数指针不同,委托是面向对象和类型安全的。

      通过将委托与命名方法或匿名方法关联,可以实例化委托。与之关联的匿名方法必须除了方法名之外参数类型、参数个数、参数顺序和返回值都必须和声明的委托类型保持一致。
对于可隐式转换的参数和返回值类型处理可查阅 MSDN。

      看看《.NET 大局观》第2版中对委托的定义:委托是指向方法的一个安全可靠的指针。所有 delegate 都继承自一个共同的 System.Delegate 类型,通常用于事件的处理和回调(callbacks)。每个委托都关联一系列成员,称为调用列表(invocation list)。一旦委托被调用,列表中的每一个成员也都会被调用,并获得委托所收到的参数。

二、委托是什么?

      上面已经提到了什么是委托,现在又反问了委托是什么,这不是吃饱饭没事做忽悠人吗?
      当然不是,上面的问题是从委托本身狭小的范围来说事的,现在是要在超出委托定义的范围来说明。

      为了看清委托的真面目,我们来创建一个委托类型

namespace  DelegateAndEventTest
{
    
public   delegate   void  SampleDelegate( string  message);
}


      再用 ildasm 打开编译后的 dll 查看生成的 IL 代码,结果如下:

      

      没有搞错,委托其实也只是一个类,它派生自 System.MulticastDelegate。

      既然委托是一个类,那它实例化出来的当然也是一个对象,它存储了一个类的方法的引用。这点对于用惯了 .NET 或者 JAVA 的用户来说不是很好理解,因为在他们的潜意识中已经把方法当成了语言的语法特性,它们可以被定义,被调用,但却不是数据类型,其实这样理解也是正确无误的,但却对我们理解方法引用造成一些障碍,直到有一天看《JS 权威指南》中对函数的介绍时忽然茅塞顿开,因为在 js 中它把函数也当成一种数据类型处理,如果在委托中也把方法当成一种数据类型看待呢?一个方法同样同样也是需要被分配一定范围内存空间的,同样也可以通过地址访问,委托存储的就是它的地址引用。

      总结:委托是一个定义签名的类型,即方法的返回值类型和参数列表类型。可以使用委托类型来声明一个变量,该变量可以引用与委托签名相同的所有方法。

      C#高级编程(第4版)中提到:理解委托的一种好方式是把委托的作用当作是给方法签名指定名称。

三、什么是事件

      下面的解释来自MSDN,基本上已经能说明事件的概念了:

      .NET 使用 event 关键字来指定事件。

      事件是类在发生其关注的事情时用来提供通知的一种方式。例如,封装用户界面控件的类可以定义一个在用户单击该控件时发生的事件。控件类不关心单击按钮时发生了什么,但它需要告知派生类单击事件已发生。然后,派生类可选择如何响应。
事件使用委托来为触发时将调用的方法提供类型安全的封装。委托可以封装命名方法和匿名方法。

      事件具有以下特点:
      * 事件是类用来通知对象需要执行某种操作的方式。
      * 尽管事件在其他时候(如信号状态更改)也很有用,事件通常还是用在图形用户界面中。
      * 事件通常使用委托事件处理程序进行声明。
      * 事件可以调用匿名方法来替代委托。

      事件处理程序委托的标准签名定义一个没有返回值的方法,其第一个参数的类型为 Object,通常命名为 sender,它引用引发事件的实例,第二个参数从 EventArgs 类型派生,通常命名为 e,它保存事件数据。如果事件不生成事件数据,则第二个参数只是 EventArgs 的一个实例。否则,第二个参数为从 EventArgs 派生的自定义类型,提供保存事件数据所需的全部字段或属性。

四、 一个关于委托与事件的例子

      下面是一个关于一个人从一个地方走到另一个地方的例子,Person 中包含一个方法 Move(),同时 Move() 会触发两个事件,一个是 OnBeginMove (在开始移动时发生),另一个是 OnEndMove (在到达目的地时发生),为了使其更像一个人,我们给它加上一个 Name 属性。

Person 类源代码:

None.gif using  System;
None.gif
None.gif
namespace  DelegateAndEvent
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 声明一个委托,用于代理一系列"无返回"及"不带参"的自定义方法
InBlock.gif    
/// </summary>
InBlock.gif    
/// <param name="sender">事件源</param>
ExpandedSubBlockEnd.gif    
/// <param name="e">不包含任何事件数据的 EventArgs</param>

InBlock.gif    public delegate void MyEventHandler(object sender, EventArgs e); 
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 人类
ExpandedSubBlockEnd.gif    
/// </summary>

InBlock.gif    public class Person
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 在开始移动时发生
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public event MyEventHandler OnBeginMove;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 在到达目的地时发生
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public event MyEventHandler OnEndMove;
InBlock.gif
InBlock.gif        
private string _name;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 名字
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public string Name
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gifreturn _name; }
ExpandedSubBlockStart.gifContractedSubBlock.gif            
set dot.gif{ _name = value; }
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 移动
InBlock.gif        
/// <remarks>封装了触发事件的方法</remarks>
InBlock.gif        
/// </summary>
ExpandedSubBlockEnd.gif        
/// <param name="place">目的地</param>

InBlock.gif        public void Move(Place place)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
// OnBeginMove 事件在这里被触发了
InBlock.gif
            if (OnBeginMove != null)
InBlock.gif                OnBeginMove(
this, EventArgs.Empty);
InBlock.gif
InBlock.gif            OnMove(place);
InBlock.gif
InBlock.gif            
// OnEndMove 事件在这里被触发了
InBlock.gif
            if (OnEndMove != null)
InBlock.gif                OnEndMove(
this, EventArgs.Empty);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
private void OnMove(Place place)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            Console.WriteLine(
"我走啊走啊走啊走dot.gif.");
InBlock.gif            Console.WriteLine(
"我已经走到 x={0} y={1} 的位置", place.X, place.Y);
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}


Place 类源代码:

None.gif using  System;
None.gif
None.gif
namespace  DelegateAndEvent
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
public class Place
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
private int _x;
InBlock.gif        
private int _y;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
public Place() dot.gif{ }
InBlock.gif
InBlock.gif        
public Place(int x, int y)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
this._x = x;
InBlock.gif            
this._y = y;
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public int X
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gifreturn _x; }
ExpandedSubBlockStart.gifContractedSubBlock.gif            
set dot.gif{ _x = value; }
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public int Y
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gifreturn _y; }
ExpandedSubBlockStart.gifContractedSubBlock.gif            
set dot.gif{ _y = value; }
ExpandedSubBlockEnd.gif        }

InBlock.gif    
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}


客户端原代码:

None.gif using  System;
None.gif
None.gif
namespace  DelegateAndEvent
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
public class Program
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
static void Main(string[] args)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
// 创建一个 Person 的新实例
InBlock.gif
            Person Yyw = new Person();
InBlock.gif
InBlock.gif            
// 将事件与委托绑定
InBlock.gif            
// 这里使用了命名委托
InBlock.gif
            Yyw.OnBeginMove += new MyEventHandler(Yyw_OnBeginMove);
InBlock.gif            
// 这里使用了匿名委托(C# 2.0 的新特性)
InBlock.gif
            Yyw.OnEndMove += delegate(System.Object sender, System.EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                Console.WriteLine(
"我已经走到了尽头");
ExpandedSubBlockEnd.gif            }
;
InBlock.gif            
InBlock.gif            Place place 
= new Place(1020);
InBlock.gif            
InBlock.gif            
// 到那边去
InBlock.gif
            Yyw.Move(place);
InBlock.gif
InBlock.gif            Console.Read();
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
static void Yyw_OnBeginMove(object sender, EventArgs e)
ExpandedSubBlockStart.gif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值