写在最前面:
无论是用什么编程语言编写应用程序,都会涉及到函数调用之间的问题。而调用过程可以分为两种,一种是主动请求调用,一种是被动等待调用。这也就是我们常说的调用与回调。下面我将说明DotNet(C#)关于函数回调的实现分析。
一、DotNet(C#)函数回调。
在DotNet中实现函数调用是通过委托(delegate)实现的,首先你要声明委托原型:
delegate void Notify( int newValue );
这样就声明了一个委托,那到底什么是委托呢?其实委托就是一个回调函数(更确切的说委托是一个安全的函数指针)。当需要回调的时候。可以调用委托的成员函数 Invoke 就可以实现调用你设置的回调函数。这时Invoke会自动根据你声明的委托形式进行调用。在这里我们举一个例子,压力计、报警器的例子:
当压力计的压力指数变化的时候,报警器会报警,并打印出变化的压力值。代码如下
无论是用什么编程语言编写应用程序,都会涉及到函数调用之间的问题。而调用过程可以分为两种,一种是主动请求调用,一种是被动等待调用。这也就是我们常说的调用与回调。下面我将说明DotNet(C#)关于函数回调的实现分析。
一、DotNet(C#)函数回调。
在DotNet中实现函数调用是通过委托(delegate)实现的,首先你要声明委托原型:

当压力计的压力指数变化的时候,报警器会报警,并打印出变化的压力值。代码如下
1
using System;
2
using System.Collections.Generic;
3
using System.Text;
4
5
namespace ConsoleApplication2
6
{
7
// 这里声明委托
8
public delegate void Notify( int newValue );
9
10
// 压力计
11
class Piezometer
12
{
13
// 压力值
14
private int m_PressureNumber;
15
public int PressureNumber
16
{
17
get
18
{
19
// 返回当前压力值
20
return this.m_PressureNumber;
21
}
22
set
23
{
24
// 设置新的压力值
25
this.m_PressureNumber = value;
26
// 判断是否有人注册该事件,如果有就调用并传入新的压力值。
27
if (OnPressureChanged != null)
28
{
29
// 这里就是DotNet框架实现的委托好处,它可以根据
30
// 声明的形式自动匹配调用的参数表和返回值。
31
32
// 调用回调事件,将新的压力值传入
33
OnPressureChanged.Invoke(value);
34
}
35
}
36
}
37
38
// 声明一个事件,当压力值变化的时候触发该事件
39
public event Notify OnPressureChanged;
40
}
41
42
// 报警器
43
class Alerter
44
{
45
// 设置监听的压力计
46
public void Listen(Piezometer piezometer)
47
{
48
// 注册压力计压力变化事件
49
piezometer.OnPressureChanged += new Notify(OnChanged);
50
}
51
// 这里就是压力计变化后调用的函数
52
public void OnChanged(int newValue)
53
{
54
// 打印出新的压力值
55
Console.WriteLine(string.Format("New PressureNumber is {0}.", newValue));
56
}
57
}
58
59
class Program
60
{
61
static void Main(string[] args)
62
{
63
Alerter alerter = new Alerter();
64
Piezometer piezometer = new Piezometer();
65
66
// 安装压力计,进行监听
67
alerter.Listen(piezometer);
68
69
// 设置新的压力值,报警器就会打印出新的压力值。
70
piezometer.PressureNumber = 10;
71
}
72
}
73
}
74

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

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

根据上面的代码我们可以实现自己的自定义事件。(感叹:DotNet框架真是太便利了,声明委托之后,委托的调用方法会自动变成声明的形式。C++就不支持这种操作。下面我会讲一下C++的实现方法。)
在这里我们用Reflector反编译一下Delegate类,该类是委托类型的基类。然而,只有系统和编译器可以显式地从 Delegate 类或 MulticastDelegate 类派生。此外,还不允许从委托类型派生新类型。Delegate 类不是委托类型,该类用于派生委托类型。而我们实现的都是MulticastDelegate派生类型,这样就可以产生一个委托多播的类型。最基本的实现 是
1
protected virtual object DynamicInvokeImpl(object[] args)
2
{
3
RuntimeMethodHandle methodHandle = new RuntimeMethodHandle(this.GetInvokeMethod());
4
RuntimeMethodInfo methodBase = (RuntimeMethodInfo) RuntimeType.GetMethodBase(Type.GetTypeHandle(this), methodHandle);
5
return methodBase.Invoke(this, BindingFlags.Default, null, args, null, true);
6
}
7

2

3

4

5

6

7

当委托被调用时会产生一个运行时方法对象。并通过运行时对象调用
1
[MethodImpl(MethodImplOptions.InternalCall), DebuggerStepThrough, DebuggerHidden]
2
private extern object _InvokeMethodFast(object target, object[] arguments, ref SignatureStruct sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner);

2

通知VM进行系统执行方法体。而在多播委托里包含一个_invocationList实现了保存多个委托,使之内部可以循环调用进行广播。