深入理解委托(一)

《Microsoft .NET Framework 框架程序设计》学习笔记
 声明、创建和使用委托
 委托的内部实现
 System.Delegate 与 System.MulticastDelegate 历史
 判断两个委托对象是否相等

================
@ 声明、创建和使用委托
================
 

ContractedBlock.gif ExpandedBlockStart.gif 声明、创建和使用委托
None.gifusing System;
None.gif
using System.Windows.Forms;
None.gif
using System.IO;
None.gif
None.gif
class Set
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
private Object[] items;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public Set(Int32 numItems) dot.gif{
InBlock.gif        items 
= new Object[numItems];
ExpandedSubBlockStart.gifContractedSubBlock.gif        
for (Int32 i = 0; i < numItems; i++dot.gif{
InBlock.gif            items[i] 
= i;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// 定义一个 Feedback 委托类型,嵌套在 Set 类中。
InBlock.gif
    public delegate void Feedback(Object value, Int32 item, Int32 numItems);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public void ProcessItems(Feedback feedback) dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
for (Int32 item = 0; item < items.Length; item++dot.gif{
InBlock.gif            
// 如果指定了回调函数,则调用它们。
ExpandedSubBlockStart.gifContractedSubBlock.gif
            if (feedback != nulldot.gif{
InBlock.gif                feedback(items[item], item 
+ 1, items.Length);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
class App
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif    
static void Main() dot.gif{
InBlock.gif        
// 使用委托回调静态方法。
InBlock.gif
        StaticCallbacks();
InBlock.gif        
// 使用委托回调实例方法。
InBlock.gif
        InstanceCallbacks();
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// 使用委托回调静态方法
ExpandedSubBlockStart.gifContractedSubBlock.gif
    static void StaticCallbacks() dot.gif{
InBlock.gif        
// 创建一个有 5 个 Object 的 Set 对象。
InBlock.gif
        Set setOfItems = new Set(5);
InBlock.gif
InBlock.gif        
// 处理 items 元素,但是不给任何反馈。
InBlock.gif
        setOfItems.ProcessItems(null);
InBlock.gif        Console.WriteLine();
InBlock.gif
InBlock.gif        
// 处理 items 元素,并将反馈输出到控制台上。
InBlock.gif
        setOfItems.ProcessItems(new Set.Feedback(App.FeedbackToConsole));
InBlock.gif
InBlock.gif        
// 处理 items 元素,并将反馈输出到消息框上。  
InBlock.gif
        setOfItems.ProcessItems(new Set.Feedback(App.FeedbackToMsgBox));
InBlock.gif        Console.WriteLine();
InBlock.gif        
InBlock.gif        Set.Feedback fb 
= null;
InBlock.gif        
InBlock.gif        
// 委托链。处理 items 元素,并将反馈同时输出到控制台和消息框上。
InBlock.gif
        fb += new Set.Feedback(App.FeedbackToConsole);
InBlock.gif        fb 
+= new Set.Feedback(App.FeedbackToMsgBox);
InBlock.gif        setOfItems.ProcessItems(fb);
InBlock.gif
InBlock.gif        Console.WriteLine();
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// 静态方法。
ExpandedSubBlockStart.gifContractedSubBlock.gif
    static void FeedbackToConsole(Object value, Int32 item, Int32 numItems) dot.gif{
InBlock.gif        Console.WriteLine(
"Processing item {0} of {1}: {2} .", item, numItems, value);
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// 静态方法。
ExpandedSubBlockStart.gifContractedSubBlock.gif
    static void FeedbackToMsgBox(Object value, Int32 item, Int32 numItems) dot.gif{
InBlock.gif        MessageBox.Show(String.Format(
"Processing item {0} of {1}: {2} .",item,numItems,value));
ExpandedSubBlockEnd.gif    }

InBlock.gif   
InBlock.gif    
// 使用委托回调实例方法。
ExpandedSubBlockStart.gifContractedSubBlock.gif
    static void InstanceCallbacks() dot.gif{
InBlock.gif        
// 创建一个有 5 个 Object 的 Set 对象。
InBlock.gif
        Set setOfItems = new Set(5);
InBlock.gif
InBlock.gif        
// 处理 items 元素,并将反馈输出到文件。
InBlock.gif
        App appobj = new App();
InBlock.gif        setOfItems.ProcessItems(
new Set.Feedback(appobj.FeedbackToFile));
InBlock.gif        Console.WriteLine();
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// 实例方法。
ExpandedSubBlockStart.gifContractedSubBlock.gif
    void FeedbackToFile(Object value, Int32 item, Int32 numItems) dot.gif{
InBlock.gif        StreamWriter sw 
= new StreamWriter("Status"true);
InBlock.gif        sw.WriteLine(
"Processing item {0} of {1}: {2}.", item, numItems, value);
InBlock.gif        sw.Close();
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

简单的说:我们使用 C# 关键字 delegate 来定义委托(相当于定义了一个类型),然后用 new 操作符来构造委托的实例,需要将一个和声明的委托具有相同
签名的函数作为参数传递给委托的构造函数,最后像调用普通方法一样调用委托,不同之处这里使用的是委托变量(或者说是一个方法变量)。

 

=============
@ 委托的内部实现
=============

定义委托:

public delegate void Feedback(Object value, Int32 item, Int32 numItems);

当编译器遇到这段代码时,它会产生如下所示的一个完整的类定义:

public   class  Feedback : System.MulticastDelegate{
        
//  构造器。
         public  Feedback(Object target, Int32 methodPtr);

        
//  下面的方法和源代码中指定的函数原型一样。
         public   void   virtual  Invoke(Object value, Int32 item, Int32 numItems);

        
//  下面两个方法允许我们对委托进行异步回调。
         public   virtual  IAsyncResult BeginInvoke(Object value, Int32 item, 
                     Int32 numItems, AsyncCallback callback, Object 
object );
        
public   virtual   void  EndInvoke(IAsyncResult result);
}

 

MulticastDelegate 有中几个重要的私有字段:
_target     System.Object      指向回调函数被调用时应该操作的对象。该字段用于实例方法的回调。
_methodPtr System.Int32  一个内部的整数值(准确的将,类型为 System.IntPtr)。CLR 用它来标识回调方法。
_prev  System.MulticastDelegate 指向另一个委托对象,当一个委托对象被调用时,将现调用 _prev 委托。该字段初始时为 null。

    所有的委托都有一个接受两个参数的构造函数:一个对象引用和一个指向回调方法的整数。但在前面的源代码中我们传递的参数无论是App.FeedbackToConsole
还是 appobj.FeedbackToFile 似乎都不能通过编译。实际上编译器会分析源代码确定引用的是哪个对象的哪个方法。其中对象的引用传递给 target 参数,一个特殊的表示方法的 Int32 值(由 MethodDef 或者 MethodRef 元素标记获得)会被传递给 methodPtr 参数。对于静态方法, null 会传递给 target 参数。这两个参数会保存在相应的私有字段中。另外 _prev 字段设置为 null,该字段用于创建一个 MulticastDelegate 对象的链表,代表该委托对象的前一个委托对象。

    每个委托实际上是对方法及其调用时操作的对象的一个封装。MulticastDelegate 类定义了两个只读的共有实例属性:Target 和 Method。对于一个委托对象的
引用,Target 属性返回方法回调时操作的对象引用,如果是静态方法 Target 返回 null。Method 属性返回一个表识回调方法的 System.Reflection.MethodInfo 对象。

    编译器遇到下面的代码时:
    feedback(items[item],item + 1, items.Length);

   它产生的代码就像编译自下面的源代码一样:
    feedback.Invoke(items[item], item + 1, ites.Length);

    如果在 feedback 对象上显示调用 Invoke 方法,C# 编译器会产生变异错误,但其他的编译器可能会要求通过显示调用 Invoke 来执行回调方法。

===================================
@ System.Delegate 与 System.MulticastDelegate 历史
===================================
    刚开始设计 .NET 框架时,微软的工程师们感觉有必要提供两种不同类型的委托:一种是单播委托,一种是多播委托。他们希望用继承自 MulticastDelegate 的
类型来表示可以被链接在一起的委托对象,而用继承自 Delegate 的类型表示不可以被链接一起的委托对象。System.Delegate 被设计为一个基类,它实现了回调
一个方法所有必要的功能。MulticastDelegate 类继承自 Delegate,并且为创建 MulticastDelegate 对象链表提供了支持。
    编译器编译源代码时,它们会检查委托的签名,然后在这两个类中选择一个最合适的作为编译器产生的委托的基类型。具体来讲就是返回值为 void 的方法原型
表示的委托继承自 System.MulticastDelegate,否则继承自 System.Delegate。
    然而在 .NET 框架的测试阶段,采用两种不同的基类型给开发人员造成了很大的困惑,而且同时带来了一些限制。比如,对于许多具有返回值的方法,在很多
情况下是可以忽略这些返回值的,但是因为具有非 void 的返回值,就不能从 MulticastDelegate 类来继承它们,从而导致不能把它们放到一个委托链表中。
    为了减少开发人员的困惑,微软工程师又希望将 Delegate 和 MulticastDelegate 合并为一个类,从而允许任何委托对象都可以被放入到一个委托链表中。可惜
合并的想法在整个框架的开发周期中来得太晚了,微软担心这样会带来很多潜在的 bug,以及测试负担,所以在版本 1 中并未将二者合并。虽然这两个类并没有
合并,但是他们改变了他们的编译器,实际上所有的委托类型都继承自 MulticastDelegate 类,这样不管回调方法是否有返回值,都可以加入到委托链表中去。

======================
@ 判断两个委托对象是否相等
======================
Delegate 重写了 Object 的 Equals 虚方法,MulticastDelegate 又重写了 Delegate 的 Equals 实现。MulticastDelegate 的 Equals 方法在比较两个委托对象时会首先看它们的 _target 和 _methodPtr 是否指向同样的对象和方法,如果不这两个字段不匹配,Equals 将返回 false。如果 两个多播委托具有相同的调用列表,则为 true;否则为 false。

ContractedBlock.gif ExpandedBlockStart.gif 判断两个委托是否相等
None.gifusing System;
None.gif
delegate void D1(string str);
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public class DelegateEqualTestdot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public static void f1(string str)dot.gif{}
ExpandedSubBlockStart.gifContractedSubBlock.gif              
public static void f2(string str)dot.gif{}
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public void f3(string str)dot.gif{}
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public static void Main(string[] args)dot.gif{
InBlock.gif  D1 d1 
= new D1(f1);
InBlock.gif  D1 d2 
= new D1(f1);
InBlock.gif  D1 d3 
= new D1(f2);
InBlock.gif
InBlock.gif  
// d1 和 d2 都是对像 null (因为是静态方法)的 f1 方法。
InBlock.gif
  Console.WriteLine(d1 == d2); // true  
InBlock.gif  
InBlock.gif  
// 对象相同(null),方法不同。
InBlock.gif
  Console.WriteLine(d1 == d3); // false
InBlock.gif

InBlock.gif  D1 d4 
= new D1(new DelegateEqualTest().f3);
InBlock.gif  D1 d5 
= new D1(new DelegateEqualTest().f3);
InBlock.gif
InBlock.gif  
// 方法相同,对象不同。
InBlock.gif
  Console.WriteLine(d4 == d5);  // false
InBlock.gif  
InBlock.gif  
// 委托链表的比较。
InBlock.gif
  Console.WriteLine((d1 + d3) == (d1 + d3)); // true
InBlock.gif
  Console.WriteLine((d1 + d3) == (d3 + d1)); // false
InBlock.gif
  Console.WriteLine((d1 + d2 + d3) == (d1 + d2 + d4)); // false
InBlock.gif
InBlock.gif  
// null 比较。
InBlock.gif
  D1 d6 = null;
InBlock.gif  D1 d7 
= null;
InBlock.gif  Console.WriteLine(d5 
== d6);  // false
InBlock.gif
  Console.WriteLine(d5.Equals(d6)); // false
InBlock.gif
  Console.WriteLine(d6 == d7);  // true 参见下面说明。
InBlock.gif
  Console.WriteLine(d6.Equals(d7)); // 异常
ExpandedSubBlockEnd.gif
 }

ExpandedBlockEnd.gif}


下面的代码是 == 的内部实现,看了这段代码就知道为什么上面的 d6 == d7 会返回 true 了。也就是说两个同为 null 的委托对象比较,会返回 true。

public   static   bool   operator   == (MulticastDelegate d1, MulticastDelegate d2)
{
    
if  (d1  ==   null )
    {
        
return  (d2  ==   null );
    }
    
return  d1.Equals(d2);
}

转载于:https://www.cnblogs.com/JoeDZ/archive/2008/06/28/1231705.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值