深入理解委托(二)

=========
@ 委托链
=========
    MulticastDelegate 对象有一个私有字段 _prev,该字段指向另一个 MulticastDelegate对象的引用。这使得多个委托对象可以组合成为一个链表。
Delegate 类中定义了三个静态方法帮助我们来操作委托链表:
 

class System.Delegate{
 
// 组合 head 和 tail 所表示的链表,并返回 head。注意:head 将是最后一个被调用的委托对象。
 public static Delegate Combine(Delegate tail, Delegate head);

 
// 创建一个由委托数组表示的委托链表。注意:索引为 0 的元素将为链表头部,并将是最后一个被调用的对象。
 public static Delegate Combine(Delegate[] delegateArray);

 
// 从链表中移除一个和 value 的回调目标/回调方法相匹配的委托,新的链表头会被返回,并且将是最后一个被调用的委托对象。
 public static Delegate Remove(Delegate source, Delegate value);
}

    构造一个新的委托对象时,其 _prev 字段会被初始化为 null,这表示委托链表上没有其他对象,实际上它也一直为 null,不可改变,
当我们使用 Combine 方法时,_prev 字段会指向原先的委托链的头部,但此时创建了一个新的委托对象。
    下面通过代码演示:

ContractedBlock.gifExpandedBlockStart.gifCode
None.gif// 演示向委托链中添加,移除委托,以及委托链的执行顺序。
None.gif
using System;
None.gif
delegate void D1();
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public class DelegateCombineTestdot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public static void f1()dot.gif{ Console.Write(" <-- f1 "); }
ExpandedSubBlockStart.gifContractedSubBlock.gif              
public static void f2()dot.gif{ Console.Write(" <-- f2 " ); }
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public void f3()dot.gif{ Console.Write(" <-- [" + this.GetHashCode() + "] f3 "); }
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public static void Main(string[] args)dot.gif{
InBlock.gif  
// 创建两个回调静态方法的委托对象。
InBlock.gif
  D1 d1 = new D1(f1);
InBlock.gif  D1 d2 
= new D1(f2);
InBlock.gif  
InBlock.gif  
// 返回 d2 作为表头,d1 的 _prev 为 null,d1 和 d2 的 _target 都为 null(静态方法)。
InBlock.gif
  D1 dlist = (D1)Delegate.Combine(d1,d2);
InBlock.gif  dlist(); 
// <-- f1 <-- f2
InBlock.gif
 
InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif  
InBlock.gif  
// 返回 d1 作为表头。  
InBlock.gif
  dlist = (D1)Delegate.Combine(d2,d1);
InBlock.gif  dlist(); 
// <-- f2 <-- f1
InBlock.gif
  
InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif
InBlock.gif  
// 创建两个回调实例方法的委托对象。
InBlock.gif
  DelegateCombineTest inst1 = new DelegateCombineTest();
InBlock.gif  DelegateCombineTest inst2 
= new DelegateCombineTest();  
InBlock.gif  D1 d3 
= new D1(inst1.f3);
InBlock.gif  D1 d4 
= new D1(inst2.f3);  
InBlock.gif  
InBlock.gif  
// 实例方法,d3 的 _target 为 inst1/inst2。
InBlock.gif  
// 注:不知道该如何获取当前实例的名称如,inst1 和 inst2,这里用 GetHashCode()。
InBlock.gif
  dlist = (D1)Delegate.Combine(dlist,d3);
InBlock.gif  dlist(); 
// <-- f2 <-- f1 <-- [58225482] f3
InBlock.gif

InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif  
InBlock.gif  dlist 
= (D1)Delegate.Combine(dlist,d4); 
InBlock.gif  dlist(); 
// <-- f2 <-- f1 <-- [58225482] f3 <-- [54267293] f3
InBlock.gif

InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif
InBlock.gif  D1[] darr 
= new D1[4];
InBlock.gif  darr[
0= d1;
InBlock.gif  darr[
1= d2;
InBlock.gif  darr[
2= d3;
InBlock.gif  darr[
3= d4;
InBlock.gif  
InBlock.gif  
// 接受一个委托数组作为参数的 Combine(Delegate[] darr) 方法。
InBlock.gif
  dlist = (D1)Delegate.Combine(darr);
InBlock.gif  dlist(); 
// <-- f2 <-- f1 <-- [58225482] f3 <-- [54267293] f3
InBlock.gif

InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif
InBlock.gif  
// 从委托链上移除对象。
InBlock.gif
  dlist = (D1)Delegate.Remove(dlist, new D1(f1)); 
InBlock.gif  
// 创建对象就是为了将其从委托链中移除(看起来很奇怪吧)。
InBlock.gif
  dlist(); // <-- f2 <-- [58225482] f3 <-- [54267293] f3
InBlock.gif

InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif  
InBlock.gif  
// 由于回调方法是实例方法,用新实例的方法作为委托的回调方法,并不会被移除。
InBlock.gif
  dlist = (D1)Delegate.Remove(dlist, new D1(new DelegateCombineTest().f3)); 
InBlock.gif  dlist(); 
// <-- f2 <-- [58225482] f3 <-- [54267293] f3
InBlock.gif

InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif
InBlock.gif  
// 使用原来的实例的方法作为委托的回调方法创建委托对象,以移除委托链中相应的委托对象。
InBlock.gif
  dlist = (D1)Delegate.Remove(dlist, new D1(inst1.f3)); 
InBlock.gif  dlist(); 
// <-- f2 <-- [54267293] f3
InBlock.gif
  
InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif
InBlock.gif  
// 如果委托链中有相同的委托对象,将移除最近一次加入到委托链中的委托链,
InBlock.gif  
// 即靠近链表头的委托链。从委托链的 head 向 _prev 搜索匹配对象,
InBlock.gif  
// 将找到的第一个委托链删除。如果未找到要删除的对象,将返回传递给它的第一个参数。
InBlock.gif
  D1[] darr2 = new D1[4];
InBlock.gif  darr2[
0= new D1(f1);
InBlock.gif  darr2[
1= new D1(inst1.f3);
InBlock.gif  darr2[
2= new D1(f1);
InBlock.gif  darr2[
3= new D1(inst1.f3);
InBlock.gif  
InBlock.gif  dlist 
= (D1)Delegate.Combine(darr2);
InBlock.gif  dlist(); 
//  <-- f1  <-- [58225482] f3  <-- f1  <-- [58225482] f3
InBlock.gif

InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif
InBlock.gif  dlist 
= (D1)Delegate.Remove(dlist, new D1(inst1.f3)); 
InBlock.gif  dlist(); 
//  <-- f1  <-- [58225482] f3  <-- f1
InBlock.gif

InBlock.gif  Console.WriteLine(
"\n");
InBlock.gif
InBlock.gif  dlist 
= (D1)Delegate.Remove(dlist, new D1(f1)); 
InBlock.gif  dlist(); 
//  <-- f1  <-- [58225482] f3  
ExpandedSubBlockEnd.gif
 }

ExpandedBlockEnd.gif}
 

委托链调用描述(伪代码)

// 无返回值。
class D1 : MulticastDelegate{
    
public void virtual Invoke(){
        
// 如果该委托对象不是委托链表的 head,
        
// 那么调用前一个委托对象的 Invoke()。
        if(_prev != null){
            _prev.Invoke();
        }
        
// 调用自身的回调函数。
        _target.methodPtr();
    }


// 有返回值。
class D1 : MulticastDelegate{
    
public ReturnType virtual Invoke(){
        
// 如果该委托对象不是委托链表的 head,
        
// 那么调用前一个委托对象的 Invoke()。
        if(_prev != null){
            _prev.Invoke();
        }
        
// 调用自身的回调函数,前一个返回值将被丢弃。
        return _target.methodPtr();
    }
}

C#委托操作符
// C# 编译器会把 += 翻译为对 Delegate.Combine 方法的调用,
// -= 翻译为 Delegate.Remove 方法的调用。

ContractedBlock.gifExpandedBlockStart.gifCode
Code
using System;
delegate void D1();

public class DelegateCombineTest{
 
public static void f1(){ Console.Write(" <-- f1 "); }
              
public static void f2(){ Console.Write(" <-- f2 " ); }
 
public void f3(){ Console.Write(" <-- [" + this.GetHashCode() + "] f3 "); }

 
public static void Main(string[] args){
  
// 创建两个回调静态方法的委托对象。
  D1 d1 = new D1(f1);
  D1 d2 
= new D1(f2);  

  
// 创建两个回调实例方法的委托对象。
  DelegateCombineTest inst1 = new DelegateCombineTest();
  DelegateCombineTest inst2 
= new DelegateCombineTest();  
  D1 d3 
= new D1(inst1.f3);
  D1 d4 
= new D1(inst2.f3);
  
  
// D1 dlist = new D1(); // 注意不可以这样。  
  D1 dlist = new D1(f1);
  
  dlist 
+= d1;  
  dlist(); 
// <-- f1 <-- f1
 
  Console.WriteLine(
"\n");

  dlist 
= d1 + d2 + d3 + d4;
  dlist(); 
//  <-- f1  <-- [58225482] f3  <-- f1  <-- [54267293] f3
  
  Console.WriteLine(
"\n");

  dlist 
= dlist - (d2 + d3);
  dlist(); 
//  <-- f1 <-- [54267293] f3

  Console.WriteLine(
"\n");

  dlist 
-= d1;
  dlist();
 }
}

显示调用委托链表的委托对象
========================
@ 显示调用委托链表委托对象
========================

ContractedBlock.gifExpandedBlockStart.gifCode
None.gifusing System;
None.gif
delegate void D1();
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public class DelegateCombineTestdot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public static void f1()dot.gif{ Console.Write(" <-- f1 "); }
ExpandedSubBlockStart.gifContractedSubBlock.gif              
public static void f2()dot.gif{ Console.Write(" <-- f2 " ); }
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public void f3()dot.gif{ Console.Write(" <-- [" + this.GetHashCode() + "] f3 "); }
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public static void Main(string[] args)dot.gif{
InBlock.gif  
// 创建两个回调静态方法的委托对象。
InBlock.gif
  D1 d1 = new D1(f1);
InBlock.gif  D1 d2 
= new D1(f2);  
InBlock.gif
InBlock.gif  
// 创建两个回调实例方法的委托对象。
InBlock.gif
  DelegateCombineTest inst1 = new DelegateCombineTest();
InBlock.gif  DelegateCombineTest inst2 
= new DelegateCombineTest();  
InBlock.gif  D1 d3 
= new D1(inst1.f3);
InBlock.gif  D1 d4 
= new D1(inst2.f3);
InBlock.gif 
InBlock.gif  D1 dlist 
= new D1(f1);
InBlock.gif
InBlock.gif  dlist 
= d1 + d2 + d3 + d4;
InBlock.gif
InBlock.gif  Delegate[] darr 
= dlist.GetInvocationList();
ExpandedSubBlockStart.gifContractedSubBlock.gif  
foreach(D1 d in darr)dot.gif{
InBlock.gif   d();
InBlock.gif   Console.WriteLine(
"\n");
ExpandedSubBlockEnd.gif  }

ExpandedSubBlockEnd.gif }

ExpandedBlockEnd.gif}
 

输出:
 <-- f1
 <-- f2
 <-- [58225482] f3
 <-- [54267293] f3

 

转载于:https://www.cnblogs.com/JoeDZ/archive/2008/06/29/1231857.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值