委托和类一样的实际上,"定义一个委托"是指"定义一个新类"。委托实现为派生自基类System. Multicast Delegate的类,System.MulticastDelegate又派生自基类System.Delegate。System.Delegate又是委托类型的基类(System.Delegate类本身不是委托是一个抽象类该类用于派生委托类型)
委托类似于函数指针(在C中每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数)而委托是一个类型安全的函数指针他封装了函数的细节(返回值类型和参数个数和类型)只有符合此细节的函数才能指向这个委托
一、定义委托类型
1.1.委托声明:<修饰符> delegate 返回类型 标识符 (形参列表);
·修饰符只能是访问修饰符和new,委托类型隐式含有sealed即不能用于继承
·new修饰符仅在其他类型中声明的委托上使用,在这种情况下该修饰符表示所声明的委托会隐藏具有相同名称的继承成员
·委托类型的声明除了多个delegate关键字外非常类似与方法的声明但是委托没有方法体
·如果一个委托和一个方法具有相同的参数数目并且类型相同顺序相同参数修饰符(ref out)也相同且有相同的返回类型 则方法和委托类型是兼容的(意思为可以让这个委托指向该方法所以说委托是类型安全的函数指针)
·C#中的委托类型是名称等效的而不是结构等效的。具体说,对于两个委托类型(委托标识符不同)即使他们具有相同的参数列表和返回类型仍被认为是不同的两个委托类型。不过,这样两个变量名不同而结构上又相同的委托类型他们的实例在比较时可以认为是相等关系的
二、使用委托
2.1.创建委托对象
委托是一个引用类型因此有引用(即他的变量名)和对象(可以理解为赋予他的值或他引用的对象)
示例定义一个委托类型
public delegate void MyDel(int x);
MyDel del;//定义委托变量与其他引用类型相同并没有给他实际分配内存只有在他指向一个对象是才分配内存用于存储指向的对象的地址
dle = VoidInt;//这里假设VoidInt是一个静态无返回值且接受一个int值的函数 此时将委托指向了一个与他兼容的方法
或者 MyDel del = new MyDel(VoidInt);//用new运算符来初始化委托
MyDel del = new MyDel();dle = VoidInt;//可以为默认的构造函数相当于创建了一个指向空的委托变量 然后在给变量赋值
委托可以看成一种数据类型这种数据类型代表的是一系列与其兼容的方法 所以委托可以作为参数此时我们可以向传递一般数据类型一样将一个兼容这种委托的方法传递给一个函数
如果一个委托只指向了一个方法那么从宏观上看委托与这个方法是等效的即想调用这个方法用委托名或方法名是相同的
例如: del(123); VoidInt(123);//两者是等效的
2.2.为委托增加方法
委托变量虽然与函数指针很像但它是类型安全的并且一个委托变量可以引用多个相兼容的方法
MyDel del = new MyDel();dle = VoidInt;
dle += VoidInt2;//可以据此加多个方法
dle -= VoidInt2;//与增加方法相反这样可以移除方法
MyDel dle1 = VoidInt2;MyDel del2 = del + del1;//同种委托可以相互组合相当于将委托del和del1中的方法都添加到del2委托中
2.3.调用委托
可以想调用方法一样调用委托(而且委托还可以看成一个变量赋予一个参数不同的是他传递的是一个方法)如果调用的委托只有一个进入点(即只指向一个方法)是调用委托与调用其指向的方法没有什么不同
如果委托指向多个方法是那么调用这样的委托实例就是按顺序同步调用调用列表中所列的各个方法(这种方式调用的每个方法都使用相同的参数集即提供给委托实例的参数集 但如果这样的委托调用包括引用参数那么每个方法调用都将使用对同一变量的引用即若调用列表中有某个方法对该变量进行可更改则调用列表中排列在该方法之后的所有方法都会见到此变更)
如果委托调用包含输出参数或者一个返回值则他们最终值就是调用列表中最后一个方法调用产生的结果(即不会有多个返回值)
三、程序示例及总结
using System;public delegate int IntStr(int[] iArr);//定义一个接受一个字符串类型返回一个int类型的委托//委托可以看成一种数据类型 他表示任何与他兼容的方法的一种数据类型public class Example{ public static int ChangeValue(int[] iArr) { for (int i = 0; i < iArr.Length; i++) { iArr[i] = i; } return 0; } public static int GetLength(int[] iArr) { return iArr.Length; } public static int ReturnAdd(int[] iArr) { int result = 0; for (int i = 0; i < iArr.Length; i++) { result += iArr[i]; } return result; } public static void PrintLength(int[] iArr,IntStr ins) //此方法接受一个字符串类型和一个委托类型 //委托类型相当于一个占位符他允许任何与他结构相同的方法传递进去 { Console.WriteLine(ins(iArr)); } public static void Main(string[] args) { int[] iArr = new int[5]; PrintLength(iArr,GetLength); //GetLength是一个方法名(可以将方法名想象成为一个指向方法的指针类似于数组名) IntStr ins; ins = GetLength;//可以把方法名赋予一个与他兼容的委托 Console.WriteLine("{0},{1}",ins(iArr),GetLength(iArr)); //如果委托只指向一个方法那么他们是等效的即用委托名或者方法名调用函数是一样的 IntStr ins2 = new IntStr(GetLength);//与上面相同 从这里可以看出委托实际上是个特殊类 Console.WriteLine(ins(iArr)); Console.WriteLine(ins2(iArr)); IntStr ins1 = ReturnAdd; IntStr ins3 = ins + ins2;//此时ins3中有两个方法都为GetLength ins3 += ChangeValue;//又为其添加了一个方法 ins3 += ins1;//有添加了一个方法(可见可以通过委托添加) Console.WriteLine(ins3(iArr));//通过此发现委托会安顺序执行方法如果有返回值返回最后一个方法的值 //此时顺序执行他指向的4个方法有因为数组是引用类型数组中值在第三个方法中改变故第四个方法计算值就不为0了 Console.ReadKey(); }}