委托是寻址方法的.NET版本。在C++中,函数指针只不过是一个指向内存位置的指针,它不是类型完全的。无法判断这个指针实际指向什么,像参数和返回类型等项就更无从知晓。而.NET委托完全不同,委托是类型安全的类,它定义返回类型和参数的类型。委托类不仅包含方法的引用,也可以包含对多个方法的引用。
lambda表达式与委托直接相关。当参数是委托类型时,就可以使用lambad表达式实现委托引用的方法。
1、委托
当要把方法传送给其他方法时,需要使用委托。委托定义:
int i=int.Parse("99");
(1) 启动线程和任务——在C#中,可告诉计算机并行运行某些新的执行系列,同时运行当前的任务。这种系列就称为线程,在其中一个基类System.Threading.Thread的一个实例上使用方法Start(),便可启动一个线程。必须为计算机提供开始启动的方法的细节,即Thread类的构造函数必须带有一个参数,该参数定义线程调用的方法。
(2) 通用库类——许多库包含执行各种标准任务的代码。
(3) 事件——一般是通知代码发生什么事件。GUI编程主要处理事件。在引用事件时,运行库需要知道应执行哪个方法。这就需要把处理事件的方法作为一个参数传递给委托。
在C和C++总,只能提取函数的地址,并作为一个参数传递它。若要传递方法,就必须把方法的细节封装在一种新类型的对象中,即委托。委托只是一种特殊类型的对象,其特殊之处在于,以前定义的所有对象包含数据,而委托包含的只是一个或多个方法的地址。
1) 声明委托
C#中使用一个类时,分两个阶段。首先,需要定义这个类,即告诉编译器这个类由什么字段和方法组成。然后(除非只使用静态方法),实例化类的一个对象。当然使用委托时,也需要两个步骤。首先必须定义要使用的委托,对于委托,定义它就是告诉编译器这种类型的委托表示哪种类型的方法。然后,必须创建该委托的一个或多个实例。编译器在后台将创建表示该委托的一个类。定义委托的语法如下:
delegate void IntMethodInvoker(int x);
理解委托一个要点是它们的类型安全性非常高。在定义委托时,必须给出它所表示的方法的签名和返回类型等全部细节。
实际上,“定义一个委托”是指“定义一个新类”。委托实现为派生自基类System.MulticastDelegate的类,System.MulticastDelegate又派生自基类System.Delegate。C#编译器能识别这个类,会使用其委托语法,对此不需要了解这个类的具体执行情况。这是C#与基类共同合作,使编程更易完成的另一个范例。
2) 使用委托
private delegate string GetAString();
static void Main()
{
int x=40;
GetAString firstStringMethod=new GetAString(x.ToString);
Console.WriteLine("String is {0}",firstStringMethod());
//With firstStringMethod initialized to x.ToString();
//the above statement is equivalent to saying
//Console.WriteLine("String is {0}",x.ToString());
}
在C#中,委托在语法上总是接受一个参数的构造函数,这个参数就是委托引用一个字符串的方法来初始化firstStringMethod变量,就会产生一个编译错误。
注意:因为int.ToString()是一个实例方法(不是静态方法),所以需要指定实例(x)和方法名来正确地初始化委托。
示例用GetAString委托的一个新实例初始化GetAString类型的firstStringMethod变量:
GetAString firstStringMethod=new GetAString(x.ToString);
3) Action<T>和Func<T>委托
泛型Action<T>委托表示引用一个void返回类型的方法。
下面的代码是参数double类型,返回类型double:
delegate double DoubleOp(doule x);
除声明自定义DoubleOp之外,还可以使用Func<int T,out TResult>委托。可以声明该委托类型的变量,或者该委托类型的数组,如下所示:
Func<double,double>[] operations=
{
MathOperations.MultiplyByTwo,
MathOperations.Square
};
使用它,并将ProcessAndDisplayNumber()方法作为参数:
static void ProcessAndDisplayNumber(Func<double,double>action,
double Value)
{
double result=action(value);
Console.WriteLine("Value is {0},result of operation is {1}",
value,result);
}
4) BubbleSorter示例
冒泡排序算法非常著名,是一种简单的排序方法。它适合于一小组数字,因对于大量的数字(超过10个),还有更高效的算法。冒泡排序算法重复遍历数组,比较每一组数字,按照需要交换它们的位置,从而把最大的数字逐步移动到数组的最后。对于给int排序,进行冒泡排序的方法如下:
bool swapped=true;
do
{
swapped=false;
for(int i=0;i<sortArray.Length-1;i++)
{
if(sortArray[1]>sortArray[i+1])) //problem with this test
{
int temp=sortArray[i];
sortArray[i]=sortArray[i+1];
sortArray[i+1]=temp;
swapped=true;
}
}
}while (swapped);
5) 多播委托
调用委托的次数与调用方法的次数相同。如果要调用多个方法,就需要多次显式调用这个委托。但是,委托也可以包含多个方法。这种委托称为多播委托。如果调用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果。
可以使用返回类型为void的Action<double>委托:
class Program
{
static void Main()
{
Action<double>operations=MathOperations.MultiplyByTwo;
operations+=MathOperations.Square;
Action<double>operation1=MathOperations.MultiplyByTwo;
Action<double>operation2=MathOperations.Square;
Action<double>operations=operation1 + operation2;
}
6) 匿名方法
匿名方法是用作委托的参数的一段代码。用匿名方法定义委托的语法与前面的定义并没有区别。但在实例化委托时,便有区别。下面代码说明使用匿名方法:
using System;
namespace Wrox.ProCSharp.Delegates
{
class Program
{
static void Main()
{
string mid=",middle part,";
Func<string,string>annonDel=delegate(string param)
{
param +=mid;
param +="and this was added to the string.";
return param;
};
Console.WriteLine(anonDel("Start of string"));
}
}
}
参考
C#高级编程