委托
一、委托的使用
一个简单委托的构成
1.声明委托类型;
2.必须有一个方法包含了要执行的代码;
3.必须创建一个委托实例;
4.必须调用(invoke)委托实例;
以一个简单的计算类来介绍吧!首先,需要声明一个委托类型:
//声明委托
public delegate int Calculate(int num1,int num2);
接下来,我们来实现委托所需要实现的方法,这里我在一个静态类中实现了加减乘除:
public static class NumCalculate
{
/// <summary>
/// 加
/// </summary>
/// <param name="num1"></param>
/// <param name="num2"></param>
/// <returns></returns>
public static int Sum(int num1, int num2)
{
return num1 + num2;
}
/// <summary>
/// 减
/// </summary>
/// <param name="num1"></param>
/// <param name="num2"></param>
/// <returns></returns>
public static int subtract(int num1,int num2)
{
return num1 - num2;
}
/// <summary>
/// 乘
/// </summary>
/// <param name="num1"></param>
/// <param name="mun2"></param>
/// <returns></returns>
public static int mutiply(int num1,int num2)
{
return num1 * num2;
}
/// <summary>
/// 除
/// </summary>
/// <param name="num1"></param>
/// <param name="num2"></param>
/// <returns></returns>
public static int divide(int num1,int num2)
{
if (num2==0)
{
return 0;
}
else
{
return num1 / num2;
}
}
}
接下来,需要创建一个委托的实例:
//创建委托实例
Calculate calculate = new Calculate(NumCalculate.Sum);
最后调用委托实例,就可以了。
int num1 = 6;
int num2 = 3;
int result = 0;
result = calculate(num1, num2);
将结果输出后,可以看到result的值为:
委托的介绍
委托的原理
再来看一看刚才的委托声明:
//声明委托
public delegate int Calculate(int num1,int num2);
根据CLR via C#的解释,编译器将会将会定义一个完整的类,通过ildasm反编译刚才生成的dll,就可以看到CLR所说的由编译器生成的类:
可以看到编译器定义的类有4个方法:一个构造器、BeginInvoke、EndInvoke和Invoke;并且可以看到这个类继承自System.MulticastDelegate。其实,当我们调用委托时,其实是调用的委托的Invoke方法。当然我们显示的调用Invoke方法也是可以得。
result = calculate.Invoke(num1,num2);
上面那行代码和前面的调用代码将会得到一样的结果。
委托的执行顺序
委托支持委托链,委托链就是委托对象的集合。当调用委托实例时,将会按顺序执行委托链中的方法。不过这里需要注意,当调用返回值不为void的委托实例时,返回值将是委托链中最后一个方法的值。将上面的代码修改成如下时:
Calculate calculate = new Calculate(NumCalculate.Sum);
calculate += NumCalculate.divide;
int num1 = 6;
int num2 = 3;
int result = 0;
result = calculate(num1, num2);
运行程序,将会得到如下的结果:
为了进一步探究委托链的原理,在委托赋值完成之后加上断点。如下所示,可以看到_invovationList包含2个成员,即我们刚才添加的2个方法。当调用委托的实例时,将会按顺序执行这2个方法,并且将最后一个方法的返回值复制给result。
对于委托实例中方法的赋值和删除,可以很方便的使用+=和-=来实现,这2个操作符本质上是对Delegate类型的Combine和Remove静态方法的调用。
此外,CLR via C#还对委托的Invoke方法进行了实现,具体伪代码如下:
public Int32 Invoke(Int32 value1,Int32 value2){
Int32 result;
Delegate[] delegateSet=_invocationList as Delegate[];
if(delegateSet!=null){
//这个委托数组指定了应该调用的委托
foreach(Calculate d in delegateSet)
result=d(value1,value2);//调用每个委托
}else{ //否则就不是委托链
//该委托标识了要回调的回调单个方法,
//在指定的目标对象上调用这个回调方法
result=_methodPtr.Invoke(_target,value1,value2);
//上面这行代码接近实际的代码,
//实际发生的事情用C#是表示不出来的
}
return result;
}
通过上面的伪代码,当数组中的每个委托被调用时,其返回值被保存到result变量中。循环完成后,result变量只包含调用的最后一个委托的结果(前面的返回值会被覆盖掉),该值最后会返回给调用Invoke的代码。这也说明了上面提到的:当调用返回值不为void的委托实例时,返回值将是委托链中最后一个方法的值。
总结
1.为了创建委托实例,需要一个方法以及(对于实例方法来说)调用方法的目标;
2.委托封装了包含特殊返回类型和一组参数的行为,类似于包含单一方法的接口;
3.委托实例可以合并到一起,也可以从一个委托实例中删除另一个;
4.每个委托实例都包含一个调用列表。
本文参考CLR via C#、深入理解C#