第17章 委托
委托是一种回调函数机制,按照我的想法,这算是一个方法指针吧。首先定义委托的关键字是:deletage。例如:
delegate void Feedback(int value);
这样定义的一个委托看上去就是一个方法签名,包括返回值和参数列表。在构造Feedback委托对象时,编译器必须确保构造的方法的签名兼容于Feedback委托定义的签名。
将一个方法绑定到委托时,C#和CLR都允许引用类型的协变性和逆变性。协变性是指方法能返回从委托的返回类型派生的一个类型。逆变性指方法获取的参数可以是委托的参数类型的基类。例如:
delegate Object MyCallback(FileStream s);
完全可以构造该委托的一个实例,并使用以下的方法绑定:
string SomeMethod(Stream s);
注意:协变性和逆变性只能用于引用类型。
定义委托的代码,CLR最终生成的代码里,一个委托对应着一个类,并且这个类有4个方法:一个构造器、Invoke、BeginInvoke和EndInvoke。由于委托实际生成的代码是一个类,所以可以定义类的地方,都可以定义委托类型。
但是委托还是少定义为妙,这样会加剧程序的代码容量,最好是使用.net提供的泛型委托Action<T>()或者Func<T>(out T),这两个委托的区别在于,Action的返回值是void,而Func的返回值是最后一个参数T的类型。
回到委托的4个方法,在方法里调用委托实例时,其实也就是调用委托里的Invoke方法。例如:
private static void Counter(int from, int to, Feedback fb)
{
for (int val = from; val <= to; val++)
{
fb(val);
}
} 实际上,下面代码与上面代码最终生成的IL是一样的,也就是说,上面代码与下面代码是一样的,所以可以显式调用Invoke来调用方法。
private static void Counter(int from, int to, Feedback fb)
{
for (int val = from; val <= to; val++)
{
if (fb != null)
fb.Invoke(val);
}
}
用委托来回调许多方法(委托链)
一般多播委托用于.net的事件驱动程序里,例如WinForm或者asp.net上。例如点击一个按钮,我们可以将多个回调方法绑定到这个单击事件上,当点击这个按钮,就会依次调用这些方法。将方法添加到或者移除出委托链是调用Combine和Remove这两个方法,为了方便,C#重载了+=和-=这两个运算符,+= == Combine,-= == Remove。
C#为委托提供了一些简化语法,称为语法糖,这些语法糖是C#特有的。
简化语法1:不需要构造委托对象
C#编译器能够自己推断调用的是一个委托对象,而不用对方法的引用进行包装。
简化语法2:不需要定义回调方法
通过C#的lambda表达式来定义方法,C#编译器遇到lambda时会在该类中定义一个私有方法。这个私有方法称为:匿名函数。
本人也挺喜欢使用lambda表达式来创建匿名函数,主要是因为lambda书写的代码比较少,而且不用考虑函数的命名问题。还有一个更重要的原因是:LINQ里很常用使用到委托,而且大部分的LINQ操作只是一些简单的过滤、投影或者排序、遍历之类的操作,委托不用太复制,这时使用lambda就很有用了。
简化语法3:局部变量不需要手动包装到类中即可传给回调方法
当回调方法中需要使用类中的局部变量时,唯一的办法是定义一个新的辅助类,这个类要为我们打算传给回调代码的每一个值都定义一个字段。还好C#为我们自动完成这个工作,所以可以在回调方法里直接使用局部变量。
本文深入探讨C#中的委托概念,包括定义、使用方法以及如何利用委托实现回调机制。此外,还介绍了委托的协变性和逆变性特点,以及C#提供的简化语法。
152

被折叠的 条评论
为什么被折叠?



