[C#参考]委托机制

时间:2023-03-08 19:52:14

1. 委托概述

这是一个新的概念,但是其本质并不是什么新鲜的事物,委托本质上就是一个类。只不过一般的类是数据的集合,委托保存的是一个或者多个方法。委托是引用类型,因此委托有引用和对象,同时委托对象中包含指向方法的引用,也就是C++中的函数指针的概念。也就是说委托中的数据成员都是一些函数指针,这些函数指针指向的方法和委托类型有相同的函数签名。

[C#参考]委托机制

从上图中可以看出,委托和类基本上就是一个东西,过程都是一样的:

声明一个类型->声明该类型的引用变量(在栈中)->利用new在堆中创建实例对象,同时利用构造函数传参数,只不过委托实例利用默认的构造函数初始化调用列表->使用变量,通过引用变量,使用该类型的对象。

[C#参考]委托机制

类对象的数据部分保存的是一般的数据类型 ,委托对象的数据部分保存的是函数指针,这些函数指针挨着盘的存放,构成了调用列表

2. 声明委托类型

委托是类型,就好像类是类型一样,所以委托类型必须在被用来创建引用变量以及类型的对象之前声明。

delegate void MyDel(int x);
//其中的MyDel就是委托类型名,类似于类名

注意:委托的类型声明,不需要在类内部声明,因为委托类型和类是同一个级别的。

[C#参考]委托机制

3. 创建委托对象

委托是引用类型的,因此有引用和对象,就像上图中所示,引用变量存在栈内存中,委托对象存在堆内存中。

MyDel delVar;
//委托变量的声明,也就是委托引用

委托对象的创建:

delVar = new MyDel(myIntsObj.methodName);
dVar = new Mydel(SClass.staticMethodName);
//使用new关键字创建委托对象,同时给委托类型的默认构造函数传一个方法名作为参数。
//这个方法名就是委托对象调用列表中的第一个成员的方法的名字。

提示:在创建委托对象的时候有快捷的语句

delVar = myIntsObj.methodName;
dVar = SClass.staticMethodName;

  这是因为方法名称和其相对应的委托类型之间有隐式的转换。

[C#参考]委托机制

图中的Invocation list里面存储的都是一些函数指针。

当然上面的创建对象可以在一条语句上完成:

MyDel delVar = new MyDel(myIntsObj.methodName);
MyDel dVar = new Mydel(SClass.staticMethodName);

或者:

MyDel delVar = myIntsObj.methodName;
MyDel dVar = SClass.staticMethodName;

4. 委托赋值

由于委托是引用类型的,可以通过给它赋值来改变包含在委托变量中的引用。旧的委托对象会被垃圾回收器回收。

MyDel delVar;

delVar = myIntsObj.methodName;
.
.
delVar = SClass.staticMethodName;

  [C#参考]委托机制

注意:这里用到了C#垃圾回收器的知识。

5. 修改委托

事实上,委托是恒定的,委托对象被创建后就不会再改变。任何对委托对象的修改都是创建一个新的委托,同时按照要求修改这个新委托对象的调用列表。

组合委托:

MyDel delA = myIntsObj.methodName;
MyDel delB = SClass.staticMethodName;
MyDel delC = delA + delB;//组合调用列表

[C#参考]委托机制

委托增加和删除方法:

MyDel delVar = inst.MyM1;//创建并初始化
delVar += SCL.m3;//增加方法
delVar += X.Act;//增加方法

  [C#参考]委托机制

delVar -= SCL.m3;//从委托移除方法

  [C#参考]委托机制

6. 调用委托

试图调用空的委托对象会抛出异常,所以每次调用委托对象的时候,把委托对象和null进行比较,来判断委托对象的调用列表是否为空。

注意:

委托引用变量没有被初始化前,委托引用是null。委托对象中的调用列表为空,那么委托引用也为null。

调用方法是直接用委托引用,加上相对应的参数:

if(delVar != null)
{
delVar(55);
}

  [C#参考]委托机制

补充的知识点:

1. 当调用带返回值的委托对象时,调用列表中最后一个方法返回的值就是委托调用的返回值,调用列表中其他所有的方法的返回值都会被忽略。只是返回值被忽略,方法的操作还是有效果的(比如对全局变量的修改)。

2. 如果委托有引用参数,参数值会根据调用列表中的一个或多个方法的返回值而改变。在调用委托列表中的下一个方法时,参数的新值(不是初始值)会传给下一个方法。