C#阶段提高之---委托(delegate)
委托是什么?
委托是一种定义方法签名的类型,可以与具有兼容签名的任何方法关联。 您可以通过委托调用方法。委托用于将方法作为参数传递给其他方法。事件处理程序就是通过委托调用的方法。您可以创建一个自定义方法,当发生特定事件时某个类(例如 Windows 控件)就可以调用您的方法。通俗地讲委托如其名字,就是我要办的事情交给一个方法来办理,这个方法可以作为参数传递其他方法。
委托类型派生自 .NET Framework 中的 Delegate 类。 委托类型是 (sealed)密封的,不能从 Delegate 中派生委托类型,也不可能从中派生自定义类。 由于实例化委托是一个对象,所以可以将其作为参数进行传递,也可以将其赋值给属性。这样,方法便可以将一个委托作为参数来接受,并且以后可以调用该委托。这称为异步回调,是在较长的进程完成后用来通知调用方的常用方法。以这种方式使用委托时,使用委托的代码无需了解有关所用方法的实现方面的任何信息。此功能类似于接口所提供的封装。
以下是通过Reflector 7.0 反编译出来的微软.NET框架集中封装好的部分Delegate类的部分代码:
1: [Serializable, ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true)]
2: public abstract class Delegate : ICloneable, ISerializable
3: {
4: // Fields
5: internal object _methodBase;
6: [ForceTokenStabilization]
7: internal IntPtr _methodPtr;
8: [ForceTokenStabilization]
9: internal IntPtr _methodPtrAux;
10: [ForceTokenStabilization]
11: internal object _target;
反编译的代码仅供参考,不过大致可以看出这些定义的细节,获得delegate的设计里理念,明白为什么delegate是密封的,抽象的!
使用委托:
委托的定义与方法的定义相似,加上关键字delegate声明这是一个委托类型。同样可以存在自己的参数,形式如下:
1: public delegate void MyDelegates(string message);
委托是一种安全地封装方法的类型,它与 C 和 C++ 中的函数指针类似。 与 C 中的函数指针不同,委托是面向对象的、类型安全的和保险的。上面的这个例子是封装一个字符串作为参数返回类型为空的委托。
下面我通过一个排序的算法演示委托的使用方式,在这个演示的方法中我我提出进一步的请求完善这个排序。
首先我的详细需求是,通过委托对一个数组进行排序,这个数组可以接受任何的对象(object),第一步先实现接受Int的排序,后续会实现string,以及类对象的排序等等,至于这个排序的方式完全有我们自己定义。
先来声明一个委托,将来要接受一个方法作为参数,就是先来为我们自己设计的排序方式(方法)占个位置,后来我们自己的定义方法出来后有委托来实现。代码:
1: namespace SortDelegeter
2: {
3: public delegate int SortAnyDelegeter(object obj1,object obj2);
4: }
下面继续通过一个类实现排序的基本思路功能:这个类文件名字叫做SortAnything.cs 核心代码:
1: public class SortAnything
2: {
3: //这里是对将要排序的对象的处理
4: public SortAnyDelegeter sortAnyDelegeter;
5: public void SortAny(object[] obj,SortAnyDelegeter sortDelegate)
6: {
7: for (int i = 0; i < obj.Length-1; i++)
8: {
9: for (int j = 0; j < obj.Length - 1 - i; j++)
10: {
11: if (sortDelegate(obj[j + 1], obj[j]) < 0)
12: {
13: object temp = obj[j];
14: obj[j]=obj[j+1];
15: obj[j + 1] = temp;
16: //地方有限,节约缩进的距离
17: }} } } }
在这个类中我们首先将委托初始化,设置委托类型的变量sortAnyDelegeter,然后在排序方法中使用委托,这个排序方法传递两个参数一个是排序的对象,另一个是使用的委托对象。在11行的代码中我们开始使用委托,传入委托的两个参数。这里请注意委托的返回类型是Int,因此这里我们可以直接比较大于小于0.基本算法的内部还是基本的冒泡排序。
我们的主方法的调用中可以实现这样的代码:
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: //排序的算法使用委托完成(任意数据。字符的排序)
6: SortAnything sort = new SortAnything();
7: //int[] number = new int[] { 32, 2, 34, 2, 13, 32, 12 };
8: //这种定义方式有错误,
9: //因为int[]的数组不允许隐式转换为object[]数组
10: object[] number = { 32,2,34,2,13,32,12};
11: sort.SortAny(number, SortNumber);
12: //输出数组
13: for (int i = 0; i < number.Length; i++)
14: {
15: Console.WriteLine(number[i].ToString());
16: }
17:
18: }
19:
20: //排序数字类型的委托,实现方法
21: public static int SortNumber( object a,object b)
22: {
23: return (int)a - (int)b;
24: }
25: }
21-25行的代码具体定义比较的方法,这个方法既是当年我们处心积虑使用委托占用的位置。委托当年的占位就是为了这位同学啊!当地委托是怎么实现功能的呢?我们来看11行的代码,通过类对象加点来访问冒泡排序的方法,参数是两个即(排序对象,委托的服务对象,也就是那个被委托的方法),这既是委托,可以经现有的方法直接传给委托的实例,也就是说委托占的位置让这个方法(SortNumber)坐上了。在这里被委托的方法就是SortNumber(object a,object b)。这样就可以实现排序了。
到此为止,已经使用了委托的方式。然而先来来了一个字符串数组也要排序,总不能也是用这个方法排序吧,要是这样,排序结果肯定使你头疼。我们可以重新写一个方法对这个字符串数字排序,只需定义排序规则而已。
1: public static int SortString(object a, object b)
2: {
3: //这里不写自己的方法了,借用预定义,从简!只为了说明委托
4: return String.Compare((string)a, (string)b);
5: }
在使用的时间,顶替上面10,11行的代码改写为:
1: //object[] number = { 32,2,34,2,13,32,12};
2: object[] number = { "I ","Love","You","Do","You","Know" };
3: sort.SortAny(number, SortString);
4: //输出数组
5: for (int i = 0; i < number.Length; i++)
6: {
7: Console.WriteLine(number[i].ToString());
8: }
不仅仅如此,我们还可以无限扩展,当然这种扩展没有牵涉到设计模式,设计的有良性,只是为了演示简单的委托使用。
什么时间使用委托:
委托的基本性质就是这样的,那么什么时间使用委托呢?
- –多线程
- –自定义类(控件、通信类……(事件))
- –窗体之间回传值
- –正则表达式中替换Email掩码Replace()
- –…
这些只是委托的部分知识,深入研究委托,事件,委托的多播等等只是会更好加深委托的使用。