1、委托
委托(delegate)是一种数据结构,提供类似C++中函数指针的功能。不同的是,C++的函数指针只能够指向静态的方法,而委托除了可以指向静态的方法之外,还可以指向对象实例的方法。其实,最大的差别在于delegate是完全的面向对象且实用安全的类型。另外,delegate允许编程人员可以在执行时期传入方法的名称,动态地决定欲调用的方法。
委托的最大特点是,它不知道或不关心自己引用的对象的类。任何对象中的方法都可以通过委托动态地调用,只是方法的参数类型和返回类型必须与委托的参数类型和返回类型相匹配。这使得委托完全适合“匿名”调用。
委托主要用在两个方面:其一是CallBack(回掉)机制;其二是事件处理。
建立和使用delegate类型可按照下面的步骤进行,其后的例子给出了完整的程序代码。
1)声明样板。
首先要声明一个delegate类型:
public delegate string MyDelegate(string name);
代码中先定义一个delegate类型,名为MyDelegate,它包含一个string类型的传入参数name,一个string类型的返回值。当C#编译器编译这行代码时,会生成一个新的类,该类继承自System.Delegate类,而类的名称为MyDelegate。
从语法形式上看,定义一个委托非常类似于定义一个方法。即:
访问修饰符 delegate 类型 委托名(参数序列)
但是,方法有方法体,而委托没有方法体。因为它执行的方法是在使用委托时动态指定的。
2)定义准备调用的方法。
由于这个方法是通过delegate调用的,因此,此方法的参数类型、个数以及参数的顺序都必须和delegate类型相同。
下面的程序中定义了两个方法:FunctionA与FunctionB。这两个方法的参数和MyDelegate的类型一样,有一个string类型的传入参数,有一个string类型的返回值。
public static string FunctionA(string name)
{
......
}
public static string FunctionB(string name)
{
......
}
3)定义delegate类型的处理函数,并在此函数中通过delegate类型调用定义的方法。
在下面的例子中,处理函数的功能比较简单,仅仅输出一个字符串,字符串中包含通过MyDelegate类型调用的方法得到的输出内容。
public static void MethodA(MyDelegate Me)
{
Console.WriteLine(Me("张三"));
}
由于MyDelegate类型的定义中有一个string类型的传入参数,所以使用时也必须传入一个字符串,即:Me("张三")。
因此,如果Me指向的FunctionA,则会执行FunctionA内的程序代码,如果Me指向的是FunctionB,则会执行FunctionB内的程序代码。
4)创建实例,传入准备调用的方法名。
由于声明了一个delegate类型在编译时期会被转换成一个继承自System.Delegate的类,因此要使用delegate类型时,必须先建立delegate的实例,并把它关联到一个方法。
MyDelegate a = new MyDelegate(FunctionA);
本行代码的含义是:a指向FunctionA方法的程序代码段。
建立delegate类型的实例后,就可以直接调用处理函数,并传入delegate类型的变量。
例如:
MethodA(a);
由于a指向FunctionA的引用,所以实际执行的FunctionA中的程序代码。
【例】使用delegate
using System;
namespace test
{
//第一步:声明委托
public delegate string MyDelegate(string name);
public class test
{
//第二步:定义被调用的方法
public static string FunctionA(string name)
{
return "A say Hello to " + name;
}
public static string FunctionB(string name)
{
return "B say hello to " + name;
}
//第三步:定义delegate类型的处理函数,并在此函数中通过delegate类型调用第二步定义的方法
public static void MethodA(MyDelegate Me)
{
Console.WriteLine(Me("张三"));
}
public static void Main()
{
//第四步:创建实例,传入准备调用的方法名
MyDelegate a = new MyDelegate(FunctionA);
MyDelegate b = new MyDelegate(FunctionB);
MethodA(a);
MethodA(b);
Console.ReadLine();
}
}
}
A say Hello to 张三
B say Hello to 张三
2、事件
“事件”是指当对象发生某些事情时,向其他对象提供通知的一种方法。在C#中,事件是通过delegate实现的。
事件有两个角色:一个是事件发送方,一个是事件接收方。事件发送方是指触发事件的对象,事件接收方是指注册想在某种事件发生时被通知的对象。
举例说明,目前有很多期刊杂志,而你可以只订购感兴趣的杂志。一旦你订购了指定的杂志,当这些杂志发行时,就会将这些杂志送到你指定的地方(单位或者代办处)。此时发行杂志的出版社就称为事件发送方,你就是事件接收方。而每当杂志发行时,就触发一个发行事件。但出版社并不是直接将杂志送给你,而是委托邮局做这件事,或者说邮局是出版社的委托。
事件发送方其实就是一个对象,这个对象会自行维护本身的状态信息。当本身的状态信息变动时,便触发一个事件,并通知所有的事件接收方。
事件接收方可以注册感兴趣的事件。一般提供一个事件处理程序,以便在事件发送方触发一个事件后,会自动执行这段程序代码的内容。
事件最常见的用途是用于图形用户界面。一般情况下,每个控件都有一些事件,当用户对控件进行某些操作(如单击某个按钮)时,系统就会将相关信息告诉这些事件。
2.1声明一个事件
事件是通过delegate机制实现的,因此若要声明一个事件,首先要声明一个delegate类型,然后使用event保留字声明一个事件,并将事件名称和delegate类型关联在一起。【例】事件处理
using System;
namespace MyCollections
{
using System.Collections;
public delegate void ChangeEventHandler(object sender,EventArgs e);
public class ListWithChangeEvent:ArrayList
{
public event ChangeEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
{
Changed(this, e);
}
}
public override int Add(object value)
{
int i = base.Add(value);
OnChanged(EventArgs.Empty);
return i;
}
public override void Clear()
{
base.Clear();
OnChanged(EventArgs.Empty);
}
public override object this[int index]
{
set
{
base[index] = value;
OnChanged(EventArgs.Empty);
}
}
}
}
using System;
namespace TestEvents
{
using MyCollections;
class EventListener
{
private ListWithChangeEvent List;
public EventListener(ListWithChangeEvent list)
{
List = list;
List.Changed += new ChangeEventHandler(ListChanged);
}
private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("开始让我处理了!");
}
public void Detach()
{
List.Changed -= new ChangeEventHandler(ListChanged);
List = null;
}
}
class Test
{
public static void Main()
{
ListWithChangeEvent list = new ListWithChangeEvent();
EventListener listener = new EventListener(list);
list.Add("item 1");
list.Clear();
listener.Detach();
Console.ReadKey();
}
}
}
输出结果为:
开始让我处理了!
开始让为处理了!
例子中声明了一个delegate类型,名称为ChangedEventHandler,并带有两个参数,这有点像先指定一个邮局,并告诉邮局出版社是谁(object sender),出版社都有哪些信息(EventArgs e)。
public delegate void ChangedEventHandler(object sender,EventArgs e);
然后声明一个事件,名称为Changed,并指明用ChangedEventHandler作为委托。
public event ChangedEventHandler Changed;
这样做的目的是,当触发Changed事件时,可以通过ChangedEventHandler这个委托进行事件处理。就好像订购杂志时先要指明和哪个邮局(出版社的委托)建立联系,以便邮局接到杂志时(触发事件时)可以将杂志送给你。此处的ChangedEventHandler相当于邮局,Changed相当于订购杂志者。
2.2调用事件
{
if (Changed != null)
{
Changed(this, e);
}
}
public class ListWithChangedEvent::ArrayList
{
......
public override int Add(object value)
{
int i = base.Add(value);
OnChanged(EventArgs.Empty);
return i;
}
......
}
这样,当调用Add方法时,该方法就会调用OnChanged方法,而OnChanged方法中调用了Changed事件,所以也可以说当调用Add方法时调用了Change事件。
2.3声明事件发生时处理的方法
private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("开始让我处理了!");
}
这个方法有什么用呢?开始说过,当邮局接收到杂志后,它要进行处理,比如将杂志送到订户指定的单位等。也就是说,这个方法就是告诉邮局如何处理的。