委托实际上是类(一个貌似函数一样的类),我们已经使用函数指针很多年了——函数指针也被称为过程类型,但是它们的实现都不是类。它们是单独的函数指针的简单实例。委托是包含这些功能的类,委托类通过保留的列表来确定某些事物是否已经指派给了委托,这种算法可以理解为:“对于内部列表中的每一个回调函数,都调用函数”。委托除了支持回调函数外,还可以通过该列表来实现多播(委托链)。
说白了,就是我们把一批具有相同特征的方法,通过建立与具有同样相同特征的委托的一个实例来进行传递,以使其它地方能对这些方法进行调用。即把方法当作参数进行传递(浅显的认识,勿笑)。
初识委托
示例1:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
namespace MyDelegateTest
{
/// <summary>
/// 说明:一个简单的委托示例
/// 作者:文野
/// 联系:stwyhm.cnblogs.com
/// </summary>
// 新建一个用以输出消息的委托
public delegate void WriteMessage();
public class WriteToWeb
{
// 一个输出消息的静态方法
public static void StaticWrite()
{
HttpContext.Current.Response.Write("委托调用了一个静态方法。<br />");
}
// 一个输出消息的类实例方法
public void Write()
{
HttpContext.Current.Response.Write("委托调用了一个类实例方法。<br />");
}
}
}
调用:
protected void Page_Load(object sender, EventArgs e)
{
// 新建一个委托,回调函数是一个静态方法
WriteMessage wm = new WriteMessage(WriteToWeb.StaticWrite);
WriteToWeb w = new WriteToWeb();
// 新建一个委托并与加到先前建立的委托组成委托链,回调函数是一个类实例方法
wm += new WriteMessage(w.Write);
// 执行委托链上所有的回调函数
wm();
}
上面的示例程序首先建立了一个简单得不能再简单的用以输出的委托,下面的类中有两个与委托签名一致的方法(一个静态方法,一个类实例方法)。下面的调用过程首先建立了一个使用回调静态方法的委托,其后又建立了一个使用类实例方法的委托并与先前的委托组成了一个委托链,最后执行委托链上所有的方法。
由此可见,委托最简单的理解就是利用与方法签名一致的委托,可以把方法当作参数一样来传递,无论是静态方法还是类实例方法。
委托的秘密
从上面的类视图中我们看到,对委托的定义最终被编译成一个类,这个类中定义有4种方法:构造器,Invoke,BeginInvoke,EndInvoke。
所有的委托都继承自MulticastDelegate,而MulticastDelegate又继续至Delegate。这样我们定义的委托自然也就继承了MulticastDelegate的字段、属性和方法。在继承得到的所有成员中,有3个最重要的字段:
1、_target:指向调用回调函数时应该操作的对象。该字段用于实例方法的回调。
2、_methodPtr:一个内部的整数值,CLR用它来识别回调的方法。
3、_prev:指向另一个委托对象。
当编译器知道我们在构造的是一个委托时,它会分析源代码来确定要引用哪个对象和方法。其中对象引用会被传递给_target(对于静态方法,_target被置为null),一个特殊的标识方法的Int32值会被传递给_methodPtr,_prev在构造器中被置为null,它被用于在委托链中记录下一个委托的引用。
每个委托对象实际上是对方法及其调用操作的一个包装,MulticastDelegate中定义了两个只读属性,Target和Method,Target(其实就是前面的_target字段)属性返回一个方法回调时操作的对象的引用,Method属性返回一个标识回调方法的System.Reflection.MethodInfo对象。
委托链
前面介绍过MulticastDelegate中有一个_prev的私有字段,这个字段指向另一个MulticastDelegate对象的引用,这样就实现了委托链(其实与我们在学链表时的实现方式是一致的)。
当委托链表被调用时,它首先会调用委托中在其前面的委托对象,这里如果被调的回调方法具有返回值,将被丢失,委托链只会返回最后一次调用回调方法的返回值。
委托示例
这是一个我自认为比较经典的委托示例(给排序算法传递一个动态比较的函数)。
using System;
using System.Collections.Generic;
using System.Text;
namespace MyDelegateTest
{
/// <summary>
/// 说明:给排序算法传递一个动态比较函数的委托示例
/// 作者:文野
/// 联系:stwyhm.cnblogs.com
/// </summary>
// 进行排序的委托
public delegate bool Compare(int left, int right);
public class DelegateSample
{
private int[] items;
public int[] Items
{
set { items = value; }
get { return items; }
}
// 比大
public bool Greater(int left, int right)
{
return left > right;
}
// 比小
public bool Less(int left, int right)
{
return !Greater(left, right);
}
public void Sort(Compare compare)
{
for (int i = 0; i < items.Length-1; i++)
{
for (int j = i + 1; j < items.Length; j++)
{
if (compare(items[i], items[j]))
{
int tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}
}
}
}
}
调用页面
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using MyDelegateTest;
public partial class Sort : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
DelegateSample sample = new DelegateSample();
sample.Items = GetItems();
// 使用降序
sample.Sort(new Compare(sample.Less));
PrintItems(sample);
}
private void PrintItems(DelegateSample sample)
{
for (int i = 0; i < sample.Items.Length; i++)
{
Response.Write(sample.Items[i] + "<br/>");
}
}
private int[] GetItems()
{
string[] str = this.TextBox1.Text.Split(",".ToCharArray());
int[] items = new int[str.Length];
for (int i = 0; i < str.Length; i++)
items[i] = int.Parse(str[i]);
return items;
}
protected void Button2_Click(object sender, EventArgs e)
{
DelegateSample sample = new DelegateSample();
sample.Items = GetItems();
// 使用升序
sample.Sort(new Compare(sample.Greater));
PrintItems(sample);
}
}
效果图:
降序
升序
参考资料:
《.NET框架程序设计》
《Visual Basic .Net Power Coding》
本文源码: