好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

委托与实践

委托与实践

分类索引: C# 语言和运行时剖析--前言

元旦休假了一段时间,所以没有更新,现在开始恢复更新,一周一到两章。

之前的章节在知识点的组织结构上更多参考了《CLR Via C#》这本书。从这章开始,打算脱离这本书的章节结构,来完全讲一些自己的总结。不过在代码实例上还是会引用这本书的案例。

委托的概念

一.定义:委托是一种封装方法的类型, 通过委托可以调用方法,相当于C/C++中函数指针的概念. 但不同的是,委托不光是一个函数地址, 而是一个面向对象的类型. 其中封装了指向对象的实例和方法。

二.要点:委托相对于其他类型来说相对抽象,因为其中有很多特殊的封装。

使用委托时,不能用 delegate 来定义一个实例,而必须先使用 delegate 来定义一个新的类型(真正使用的委托类型),这个类型提供了委托所需要指向的方法的模板.然后用这个自定义类型再申明实例使用。 委托的实例不仅封装了一个方法的引用,而且可以形成委托链,对聚合的多个方法进行链式调用。 FCL提供了多个已封装的常用委托类型,如 Action ,  Func ,  EventHandler 等,善于利用这些已封装的委托类型,特别是泛型委托,可以实现更好的设计。 委托虽然是封装方法调用的类型,但他可以作为其他方法的参数以及局部变量被调用,这也是一个较难理解的知识点。 委托有多种调用方式,在.NET 3.0之后的版本,可以使用Lambda表达式来编码较为简单的委托封装。 委托可以封装其他类型的实例方法,也可以封装静态方法。

三.代码示例

 internal   sealed   class   DelegateIntro {
     //  申明一个委托类型;此类型的实例指向一个采用Int32参数,以及返回void的方法  
    internal   delegate   void   Feedback(Int32 value);

     public   static   void   Go() {
      StaticDelegateDemo();
      InstanceDelegateDemo();
      ChainDelegateDemo1(  new   DelegateIntro());
      ChainDelegateDemo2(  new   DelegateIntro());
   }

     ///   <summary> 
    ///   静态方法封装
     ///   </summary> 
    private   static   void   StaticDelegateDemo() {
      Console.WriteLine(  "  ----- Static Delegate Demo -----  "  );
      Counter(  1 ,  3 ,  null  );
      Counter(  1 ,  3 ,  new   Feedback(DelegateIntro.FeedbackToConsole));
      Counter(  1 ,  3 ,  new  Feedback(FeedbackToMsgBox));  //   "Program." is optional 
       Console.WriteLine();
   }

      ///   <summary> 
     ///   实例方法封装
      ///   </summary> 
    private   static   void   InstanceDelegateDemo() {
      Console.WriteLine(  "  ----- Instance Delegate Demo -----  "  );
      DelegateIntro di  =  new   DelegateIntro();
      Counter(  1 ,  3 ,  new   Feedback(di.FeedbackToFile));

      Console.WriteLine();
   }

      ///   <summary> 
     ///   委托链实例一
      ///   </summary> 
     ///   <param name="di"></param> 
    private   static   void   ChainDelegateDemo1(DelegateIntro di) {
      Console.WriteLine(  "  ----- Chain Delegate Demo 1 -----  "  );
      Feedback fb1  =  new   Feedback(FeedbackToConsole);
      Feedback fb2  =  new   Feedback(FeedbackToMsgBox);
      Feedback fb3  =  new   Feedback(di.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));
      Counter(  1 ,  2  , fbChain);
   }

      ///   <summary> 
     ///   委托链示例2
      ///   </summary> 
     ///   <param name="di"></param> 
    private   static   void   ChainDelegateDemo2(DelegateIntro di) {
      Console.WriteLine(  "  ----- Chain Delegate Demo 2 -----  "  );
      Feedback fb1  =  new   Feedback(FeedbackToConsole);
      Feedback fb2  =  new   Feedback(FeedbackToMsgBox);
      Feedback fb3  =  new   Feedback(di.FeedbackToFile);

      Feedback fbChain  =  null  ;
      fbChain  +=  fb1;
      fbChain  +=  fb2;
      fbChain  +=  fb3;
      Counter(  1 ,  2  , fbChain);

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

     private   static   void  Counter(Int32  from  , Int32 to, Feedback fb) {
        for  (Int32 val =  from ; val <= to; val++ ) {
           //   If any callbacks are specified, call them 
          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();
   }
} 

           

委托的使用

一.委托的一般调用

使用场景:如上一节中的代码示例可作为委托链的一般调用案例。 使用顺序: 定义委托类型。 定义委托实例。 封装引用方法。 通过委托实例来调用封装方法。

二.委托链的一般调用

使用场景:如上一节中的代码示例可作为委托链的一般调用案例。 使用方法: 定义委托类型。 定义委托实例。 封装多个引用方法或者从委托链中删除方法引用。 通过委托实例来实现链式调用方法。 使用GetInvocationList()获取委托链的信息,示例如下:

nternal  static   class   GetInvocationList {
     //   Define a Light component. 
    internal   sealed   class   Light {
        //   This method returns the light's status. 
       public   String SwitchPosition() {
           return   "  The light is off  "  ;
      }
   }

     //   Define a Fan component. 
    internal   sealed   class   Fan {
        //   This method returns the fan's status. 
       public   String Speed() {
           throw   new  InvalidOperationException( "  The fan broke due to overheating  "  );
      }
   }

     //   Define a Speaker component. 
    internal   sealed   class   Speaker {
        //   This method returns the speaker's status. 
       public   String Volume() {
           return   "  The volume is loud  "  ;
      }
   }

     //   Definition of delegate that allows querying a component's status. 
    private   delegate   String GetStatus();

     public   static   void   Go() {
        //   Declare an empty delegate chain. 
      GetStatus getStatus =  null  ;

        //   Construct the three components, and add their status methods 
        //   to the delegate chain. 
      getStatus +=  new  GetStatus( new   Light().SwitchPosition);
      getStatus  +=  new  GetStatus( new   Fan().Speed);
      getStatus  +=  new  GetStatus( new   Speaker().Volume);

        //   Show consolidated status report reflecting 
        //   the condition of the three components. 
       Console.WriteLine(GetComponentStatusReport(getStatus));
   }

     //   Method that queries several components and returns a status report 
    private   static   String GetComponentStatusReport(GetStatus status) {

        //   If the chain is empty, there is nothing to do. 
       if  (status ==  null )  return   null  ;

        //   Use this to build the status report. 
      StringBuilder report =  new   StringBuilder();

        //   Get an array where each element is a delegate from the chain.
        //  此处status是一个GetStatus委托的实例,封装了一个委托链 
      Delegate[] arrayOfDelegates =  status.GetInvocationList();

        //   Iterate over each delegate in the array.  
       foreach  (GetStatus getStatus  in   arrayOfDelegates) {

           try   {
              //   Get a component's status string, and append it to the report. 
            report.AppendFormat( "  {0}{1}{1}  "  , getStatus(), Environment.NewLine);
         }
           catch   (InvalidOperationException e) {
              //   Generate an error entry in the report for this component. 
            Object component =  getStatus.Target;
            report.AppendFormat(
                 "  Failed to get status from {1}{2}{0}   Error: {3}{0}{0}  "  ,
               Environment.NewLine,
               ((component  ==  null ) ?  ""  : component.GetType() +  "  .  "  ),
               getStatus.Method.Name, e.Message);
         }
      }

        //   Return the consolidated report to the caller. 
       return   report.ToString();
   }
} 

三.异步调用委托

通过delegate封装的委托类型所派生的实例会自动生成Invoke(), BeginInvoke(), EndInvoke()方法,由此可见可以异步的调用委托。 异步调用委托的使用顺序是: 定义委托类型。 定义委托实例。 封装委托方法。 使用BeginInvoke()异步调用委托方法,并指定一个 AsyncCallback 委托类型的实例方法,例如Callback(),作为异步操作的回调操作方法。 在Callback()中根据BeginInvoke()执行的结果(作为 IAsyncResult 类型的参数传入),在Callback()中调用EndInvoke()方法。 实现异步调用委托的同时,需要跟其他系统委托类型打交道,也增加了理解难度,请注意。 接下来,我们省略定义委托类型这一步,使用一个 Action 委托(可以理解为一个自定义委托类型)来演示异步调用委托。

 class   Program
    {
          static   void  Main( string  [] args)
        {
              //  Action委托的异步委托 
            Action< int > action =  Speak;
            AsyncCallback callback = new   AsyncCallback(CallBackMethod);
            action.BeginInvoke(  10  , callback, action);
            Console.WriteLine(  "  当前线程ID:{0}  "  , Thread.CurrentThread.ManagedThreadId.ToString());

            Console.ReadKey();
           
        }


          //  回调方法 
         static   void   CallBackMethod(IAsyncResult ar)
        {
            
            Action < int > a = (Action< int > )ar.AsyncState;
            a.EndInvoke(ar);
        }


          //  被委托方法 
         static   void  Speak( int   i)
        {
              string  str =  string .Format( "  The number is {0}  "  ,i);
            Console.WriteLine(  "  当前线程ID:{0}  "  ,Thread.CurrentThread.ManagedThreadId.ToString());
            Thread.Sleep(TimeSpan.FromSeconds(  3  ));
            Console.WriteLine(str);
        }
    } 

委托实践

一.在实践中常用的FCL中已定义的委托类型

EventHandler :FCL中各种前端控件中的事件,基本都用这个委托类型封装。我们在编码中的自定义事件,如果没有特殊的传递需求( EventArgs ),也可以直接使用这个委托类型。 Action <T>:此委托类型适用于各种不需要返回数据的方法类型封装,支持泛型,最多支持十六个输入参数 。 Func <in T1, out T2):此委托类型适用于各种需要返回数据的方法类型封装,但不支持带有 ref ,  out 修饰符参数的方法封装。 各种CallBack回调函数封装:例如 AsyncCallback,Wait Callback 等。 其他封装:例如用于查询的 Predicate <T>, 用于转换的 Converter <T1,T2>, 用于比较的 Comparison <T>等。

二.使用Lambda表达式

Lambda表达式可以理解成为是一种对匿名方法的封装,这个主要在后面的进阶特性中讲。 因为委托是对方法和方法组的封装,所以委托封装的对象自然可以用Lambda表达式来编写,这个在一些使用委托作为参数的方法中常用。 列举几个数组Array中的方法和线程池中的异步方法调用作为示例:

 internal   static   class   AnonymousMethods {
     public   static   void   Go() {
        //   Create and initialize a String array 
      String[] names = {  "  Jeff  " ,  "  Kristin  " ,  "  Aidan  "   }; 

        //   Get just the names that have a lowercase 'i' in them. 
      Char charToFind =  '  i  '  ; 
      names  = Array.FindAll(names,  delegate (String name) {  return  (name.IndexOf(charToFind) >=  0  ); });
       
        //   Convert each string's characters to uppercase 
      names = Array.ConvertAll<String, String>(names,  delegate (String name) {  return   name.ToUpper(); });
      
        //   Sort the names  
      Array.Sort(names,  delegate (String name1, String name2) {  return   String.Compare(name1, name2);});

      Array.Sort(names, (x1, x2)  => { x1 =  "  1  " ;  return   string  .Compare(x1, x2); }); 

        //   Display the results  
      Array.ForEach(names,  delegate  (String name) { Console.WriteLine(name); }); 
   }

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

     private   sealed   class   AClass2 {
        private   static   void   UsingLocalVariablesInTheCallbackCode(Int32 numToDo) {

           //   Some local variables 
         Int32[] squares =  new   Int32[numToDo];
         AutoResetEvent done  =  new  AutoResetEvent( false  );

           //   Do a bunch of tasks on other threads 
          for  (Int32 n =  0 ; n < squares.Length; n++ ) {
            ThreadPool.QueueUserWorkItem(
                 delegate  (Object obj) {
                  Int32 num  =  (Int32)obj;

                    //   This task would normally more time consuming 
                  squares[num] = num *  num;

                    //   If last task, let main thread continue running 
                   if  (Interlocked.Decrement( ref  numToDo) ==  0  )
                     done.Set();
               }, n);
         }

           //   Wait for all the other threads to finish 
          done.WaitOne();

           //   Show the results 
          for  (Int32 n =  0 ; n < squares.Length; n++ )
            Console.WriteLine(  "  Index {0}, Square={1}  "  , n, squares[n]);
      }
   }
} 

 

 

分类:  NET01-C# 语言和运行时剖析

标签:  C# ,  CLR

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于委托与实践的详细内容...

  阅读:36次