其实这部分内容对于 .NET程序员而言应该是老生常谈了,但鉴于团队内常有成员对委托和事件感到混乱,因而记录下来以供分享。
委托是C#第一个版本就引入的特性,特点如下
1. 类似于C++中的函数指针;
2. 允许方法像变量一样赋值;
3. 用以作回调操作;
4. 委托以链表的方式记录委托实例;
5. 委托实例中使用的类型并不需要与委托声明中的类型完全吻合;
6. 在C#2.0中引入了匿名委托,C#3.0引入了Lambda表达式作为委托实例。
通常我们据说的委托包括了委托声明和委托实例两种概念,委托声明是声明了一个委托样式,而委托实例则是与委托声明类型相符的变量,该变量记录了一系列与委托声明形式类似的方法,如
class Program
{
public delegate void HandlerMethod();
public static void MammalsHandler()
{
}
public static void DogsHandler()
{
}
static void Test()
{
HandlerMethod handlerMammals = MammalsHandler;
handlerMammals += DogsHandler;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
中,public delegate Mammals HandlerMethod()是委托声明,声明HandlerMethod是一种委托类型,任何无参数且无返回值函数都可以被赋值给这种委托类型的实例,例如
HandlerMethod handlerMammals = MammalsHandler;
handlerMammals += DogsHandler;
对委托实例handlerMammals赋值之后,handlerMammals委托列表中记录了两个方法,当用户invoke handlerMammals时,MammalsHandler和DogsHandler方法会依次被调用,而通过handlerMammals-=DogsHandler之类的语句可以对移除委托实例中的委托列表。
通常而言,我们使用到委托是为了实现对象的回调操作,这就要求一个对象实现的委托链表赋值可以在对象外进行操作,但该委托实例的方法列表不能在对象外部被调用, 可以看到通过上述声明和实例化的方式,需要很多代码才能进行这种可见性隔离,此时事件就派上用场了。事件的使用方法如下;
public delegate void HandlerMethod();
class EventTest
{
public event HandlerMethod OnMammalHandled;
public void HandleMammal()
{
if(null!=OnMammalHandled)
OnMammalHandled();
}
}
static void Test()
{
EventTest et = new EventTest();
et.OnMammalHandled+=MammalsHandler;
et.OnMammalHandled+=DogsHandler;
et.HandleMammal();
et.OnMammalHandled-=MammalsHandler;
et.OnMammalHandled-=DogsHandler;
}
public static void MammalsHandler()
{
}
public static void DogsHandler()
{
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
与上一个例子相关不大,但在这个例子中,事件OnMammalHandled作为一个代理,其在类实例之外只允许通过+=/-=方法进行赋值,而在类内等同于完整的委托实例,通过引入事件,可以完美地实现我们需要的回调方法效果,即对外部只具赋值可视性,对内部具有全部可视性。
同样,事件的赋值也可以使用匿名函数或者Lambda表达式,例如
static void Test()
{
EventTest et = new EventTest();
et.OnMammalHandled+=delegate(){};
et.OnMammalHandled+=()=>{};
et.HandleMammal();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
通过强大的委托、事件匿名函数和Lambda表达式等特性,我们可以使回调代码变得清晰简单,并且可读性更高。