深入探索面向对象事件(Delegate)机制

本文详细介绍了在C#中如何使用委托实现函数回调,并通过一个压力计与报警器的实例来展示具体的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 写在最前面:
        无论是用什么编程语言编写应用程序,都会涉及到函数调用之间的问题。而调用过程可以分为两种,一种是主动请求调用,一种是被动等待调用。这也就是我们常说的调用与回调。下面我将说明DotNet(C#)关于函数回调的实现分析。

一、DotNet(C#)函数回调。
        在DotNet中实现函数调用是通过委托(delegate)实现的,首先你要声明委托原型:
delegate void Notify( int newValue );
这样就声明了一个委托,那到底什么是委托呢?其实委托就是一个回调函数(更确切的说委托是一个安全的函数指针)。当需要回调的时候。可以调用委托的成员函数 Invoke 就可以实现调用你设置的回调函数。这时Invoke会自动根据你声明的委托形式进行调用。在这里我们举一个例子,压力计、报警器的例子:

当压力计的压力指数变化的时候,报警器会报警,并打印出变化的压力值。代码如下
 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4
 5namespace 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

根据上面的代码我们可以实现自己的自定义事件。(感叹:DotNet框架真是太便利了,声明委托之后,委托的调用方法会自动变成声明的形式。C++就不支持这种操作。下面我会讲一下C++的实现方法。)
在这里我们用Reflector反编译一下Delegate类,该类是委托类型的基类。然而,只有系统和编译器可以显式地从 Delegate 类或 MulticastDelegate 类派生。此外,还不允许从委托类型派生新类型。Delegate 类不是委托类型,该类用于派生委托类型。而我们实现的都是MulticastDelegate派生类型,这样就可以产生一个委托多播的类型。最基本的实现 是

1protected 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, nulltrue);
6}

7

当委托被调用时会产生一个运行时方法对象。并通过运行时对象调用

1[MethodImpl(MethodImplOptions.InternalCall), DebuggerStepThrough, DebuggerHidden]
2private extern object _InvokeMethodFast(object target, object[] arguments, ref SignatureStruct sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner);

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值