委托揭秘

     看到button.Click+=new EventHandler(button1_Click);又忘记其中间过程了  

  委托我始终是学一遍,忘一遍,一个原因是我没用经常用道它,久而久之就忘记了,另一个原因是因为我没有深入的学习它。今天所以我决定用心搞明白它。这也是必备知识。

     首先我们看一个例子

代码
//声明一个委托类型,它的实例引用一个方法
Internal delegate void Feedback(Int32 Value);

pulbic 
sealed class Program
{
   
public static void Main()
   {
    StsticDelegateDemo();
    InstanceDelegateDemo();
    ChainDelegateDemo1(
new Program());
    ChainDelegateDemo2(
new Program());
   }

   Private 
static void StaticDelegateDemo()
   {
     Cosole.WriteLine(
"----Static Deleage Demo------");
     Counter(
1,3,null);
     Counter(
1,3,new Feedback(Program.FeedbackToConsole));
     Counter(
1,3,new Feedback(FeedbackToMsgBox));

     Console.WriteLine();
   }
  
   Private 
static void InstanceDelegateDemo()
   {
      Cosole.WriteLine(
"----Instance Delegate  Demo------");
      Program p
=new Program();
      Counter(
1,3,new Feedback(p.FeedbackToFile));
      
      Console.WriteLine();
   }

   Provate 
static void ChainDelegateDemo()
   {
       Cosole.WriteLine(
"----Chain Delegate  Demo------");
       Feedback fb1
=new Feedback(FeedbackToConsole);
       Feedback fb2
=new Feedback(FeedbackToMsgBox);
       Feedback fb3
=new Feedback(p.FeedbackToFile);
       
       Feedback fbChain
=null;
       fbChain
=(Feedback)Delegate.Combine(fbChain,fb1);
       fbChain
=(Feedback)Delegate.Combine(fbChain,fb2);
       fbChain
=(Feedback)Delegate.Combine(fbChain,fb3);
       Counter(
1,2,fbChain);
       
       Console.WriteLine();
       fbChain
=(Feedback)Delegate.Remove(fbChain,new Feedback(FeedbackToMsgBox));
       Cunter(
1,2,fbChain);
   }
    
   
private static void ChainDelegateDemo2()
   {
       Cosole.WriteLine(
"----Chain Delegate  Demo2------");
       Feedback fb1
=new Feedback(FeedbackToConsole);
       Feedback fb2
=new Feedback(FeedbackToMsgBox);
       Feedback fb3
=new Feedback(p.FeedbackToFile);
       
       Feedback fbChain
=null;
       fbChain
+=fb1;
       fbChain
+=fb2;
       fbChain
+=fb3;
       Counter(
1,2,fbChain);

       Console.WriteLine();
       fbChain
-=new Feedback(FeedbackToMsgBox);
       Cunter(
1,2,fbChain);
   }

   
private static void Counter(Int32 from,Int32 to,Feedback fb)
   {
      
for(Int32 val=from;val<=to;val++)
      {
        
if(fb!=null)
        fb(val);
      }
   }
 
   
private static void FeedbacktoConsole(Int32 value)
   {
      Console.WriteLine(
"Item="+value);
   }

   
private static void FeedbackToMsgBox(Int32 value)
   {
      MessageBox.Show(
"Item="+value);
   }

   Private 
void FeedbackToFile(Int32 value)
   {
      StreamWriter sw
=new StreamWriter("Status",true);
      sw.WriteLine(
"Item="+value);
      sw.Close();
   }
}

    一个委托要制定一个回调方法的签名。这里回调是指什么?就是调用函数。首先是用委托回调静态方法FeedbacktoConsole(Int32 value)和FeedbacktoMsgBox(Int32 value),在StaticDelegateDemo中调用Counter方法,第二次调用时,在方法调用的第三个参数传入一个新构造的Feedback委托对象。这个委托对象是一个方法的封装,这样,该方法就会通过封装期间间接进行回调。回调实例方法类似就不在多说了。

  下面我们详细看看

      internal delegate void Feedback(Int32 value);

     其实看到这样的代码我们应该把它看成一个类( 编译器会像下面定义一个完整的类)代码 //MulticastDelegate 是一个特殊类。编译器和其他工具可以从此类派生,但是您不能显式地从此类进行派生。Delegate 类也是如此 

注意:MulticastDelegate 是一个特殊类。编译器和其他工具可以从此类派生,但是您不能显式地从此类进行派生。Delegate 类也是如此

internal class Feedback:System.MulticastDelegate
{
public Feedback(Object object,IntPtr method);
//方法的原型与源代码相同
public virtual void Invoke(Int32 value);
public virtual IAsyncResult BeginInvoke(Int32
value,AsyncCallback callback,Object
object);
public virtual void EndInvoke(IAsyncResult result);
}

    这里有个构造函数,对象的引用被传值给该函数的object参数,一个特殊的标识方法的IntPtr值(从元数据标记获得)被传值给method参数。如果是静态方法那么null值就传给object。在构造函数内部,这两个参数分别保存在_target和_methodPtr私有字段内。

另外介绍MulticastDelegate3个重要的非公共字段

_target            System.Object    当委托对象封装一个实例方法时,这个字段引用的是调用回调方法时要操作的对象。

_methodPtr      System.IntPtr      一个内部的整数值,CLR用它标识要回调的方法。

_invocationList  System.Object    该字段通常为null.在构造一个委托链时,它可以引用一个委托数组。

 

例:Feedback fbInstance=new Feedback(new Program().FeedbackToFile);

                          _target             ------------->(Program对象)

fbInstance------>_methodPtr       FeedbackToFile

                         _invocationList   null

 

在看开始代码里有这一段代码,"if(fb!=null)fb(val);"这里没有名为fb的函数。因为编译器知道fb是一个引用一个委托对象的变量,所以会生成代码调用该委托对象的Invoke方法。即在编译器里是fb.Invoke(val),这里就相当于调用了原函数。

这里看到第三个字段,自然而然在心中就应该有一幅图委托数组指向很多像fbInstance这样的委托变量。例如调用上面介绍的fbChain的Invoke时,改委托就发现私有字段_invoationList不为null,就会遍历数组中所有元素,并调用每个委托封装的方法。

在来看最开始介绍的语句(button.Click+=new EventHandler(button1_Click);),这是由于C#为委托提供的语法便利造成的,这里介绍几个提供便利的语法

 1.C#允许我们制定回调方法的名称,不必构造一个委托对象封装器。

代码
internal sealed class AClass
{
  
public static void CallbackwithoutNewingAdelegateObjec
   {
      ThreadPool.QueueUserWorkItem(SomeAsyncTask,
5);
   }
  
private static void SomeAsyncTask(Object o)
   {
      Console.WriteLine(o);
   }
}

    ThreadPool类的静态方法期望接收到一个WaitCallback委托对象引用,该对象又包含一个SomeAsyncTask方法引用。由于编译器能自行进行推断,所以省略构造WaitCallback委托对象的代码,使整个代码可读性更强。其实正规写法应该为

ThreadPool.QueueUserWrokItem(new WaitCallback(SoneAsyncTask),5);

 

2.C#允许我们不需要定义回调方法

那么前面的代码可以写成

interan sealed class AClass
{
   
public static void CallbackWithoutNewingADelegateObject()
   {
      ThreadPool.QueueUserWorkItem(
delegate(Object obj){Console.WriteLine(obj);},5
   }
}

当编译器看到期望收到委托对象引用的地方使用了delegate关键字就会自动在类中定义一个新的私有方法。

编译器会将代码改写成下面那样

代码
interal sealed class AClass
{
   [compoleerGenerated]
   
private sataic WaitCallback <>9_CacheedAnnymousMethodDeleagate1;
   
public sataic void CallbackWithoutnewingADeleateObject()
   {
      
//第一次调用时,创建委托对象
      if(<>9_CacheedAnnymousMethodDeleagate1==null)
      {
          
<>9_CacheedAnnymousMethodDeleagate1=new WaitCllback(<CallbackwithoutNewingAdelegateObject>b_0);
      }
      ThreadPool.QueueUserWorkItem(
<>9_CacheedAnnymousMethodDeleagate1,5);
     
   }
   [CopilerGenerated]
   
private static void <CallbackWithoutNewingADelegateObjec>b_0(Object obj)
   {
      Console.WriteLine(obj);
   }
}

3.不需要指定回调方法参数

熟悉了1,2规则,那么我们很容易读懂下面代码了

button1.Click+=delegate(Object sender,EventArgs e){MessageBox,Show("the button was clicked");};

编译器可以允许我们不指定参数,所以可以写成

button1.Click+=delegate{MessageBox,Show("the button was clicked");};

 

 

posted @ 2010-04-12 13:04  胡佳180815  阅读(3196)  评论(5编辑  收藏  举报