看了《CLR via C#》的17章委托后,为自己做一点浅显的总结,也分享给需要的人。
.NET通过委托来提供一种回调函数机制,.NET委托提供了很多功能,例如确保回调方法是类型安全的(CLR重要目标)。委托好允许顺序调用多个方法(委托链),并且支持调用静态方法和实例方法。
委托的基本语法就不多说了。
internal delegate void Feedback(int value);
public sealed class Program{ publick static void Main(){ } private static void DelegateDemo(){ Counter(1,2,new Feedback(WriteToConsole)); Program p=new Program p(); Counter(1,2,new Feedback(WriteToMsgBox)); } private static void Counter(int from, int to, Feedback fb){ for(int i=from;i<to;i++){ if(fb!=null) fb(i); } } private static void WriteToConsole(int val){ Console.WriteLine(val); } private void WriteToMsgBox(int val){ MessageBox.Show(val) } }
和普通调用静态方法实例方法没区别,如果需要回调静态方法,那么className.FuncName(); 如果需要回调实例方法,那么Class_object.FuncName();
由于委托是类型安全的,它可以调用私用方法。
协变性和逆变性。
将一个方法绑定到委托的时候,C#和CLR都允许引用类型的协变性和逆变性,注意是引用类型哦。
delegate Object MyCallback(FileStream s);
string SomeMethod(Stream s); //引用类型允许协变性,Stream是符合逆变性
int SomeOtherMethod(Stream s); //值类型不允许协变性
协变性是指方法能返回从委托的返回类型派生的一个类型,不能用于void。逆变性是指方法获取的参数是委托的参数类型的基类。
委托链
Delegate.Combine(FirstDelegateObj,SecondDelegateObj);
FirstDelegateObj+=SecondDelegateObj; //语法糖效果,等同于上一行
上面代码就是在构造委托链。
委托链的原理大概就是:维护MulticastDelegate类中的三个重要的非公共字段中的_invocationList.这个字段维护了委托数组。
另外两个字段就是_target.当委托包装了一个静态方法时,这个字段为null.当委托对象包装了一个实例方法时,这个字段引用的是回调方法要操作的对象。
_methodPtr 一个内部的整数值,CLR用它标识要回调的方法。
在很多时候,执行委托链的过程中可能遭遇其中某个方法的阻塞和异常影响接下来的方法的执行。这个时候,我们的解决办法是使用MulticastDelegate为我们提供的GetInvocationList方法,获取由委托引用组成的数组,
每个委托引用只想链中的一个委托对象。我们可以通过遍历来执行每一个委托。
委托链执行后只会返回最后一个回调方法所返回的值、
下面给出MulticastDelegate中Invoke方法的伪代码实现,这个方法解释了委托的执行过程和原理。
public int Invoke(int value)
{
int result; Delegate[] delegateSet= _invoctionList as Delegate[]; if(delegateSet !=null) { //委托链 foreach(Feedback d in delegateSet) { result=d(value); } } else { //在执行对象上调用这个回调方法 result= _methodPtr.Invoke(_target,value); //以上代码接近实际代码,但实际上C#表示不出来。 } return result; }