zoukankan      html  css  js  c++  java
  • 读<<CLR via C#>>总结(10) 详谈委托

      首先弄清楚以下几个问题:

      1,什么是委托?
        委托是一个类(可以通过查看IL代码证明),而且是类型安全的。
        委托对象相当于方法包装器,使方法能通过包装器进行间接回调。

      2,使用委托的好处?
        使用委托可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,提高了程序的可扩展性。

    一,如何使用委托

      代码如下:

    namespace DelegateDemo1
    {
        //1,声明委托
        internal delegate void PrintMyName(string s);
    
        public class Test
        {
            public static void PrintChineseName(string name)
            {
                Console.WriteLine("My chinese name is:{0}",name);  
            }
    
            public void PrintEnglishName(string name)
            {
                Console.WriteLine("My english name is:{0}", name);
            }
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                Test t = new Test();
    
                //2,创建委托对象
                PrintMyName pmn;
                pmn = new PrintMyName(Test.PrintChineseName);//或用快捷语法:PrintMyName pmn = Test.PrintChineseName
    
                //3,调用委托
                if (pmn != null)
                {
                    pmn("鲁宁");
                }
    
                pmn = t.PrintEnglishName;//快捷语法
                if (pmn != null)
                {
                    pmn("Mcgrady");
                }
                Console.ReadKey();
    
                /*程序输出结果为:
                 My chinese name is:鲁宁
                 My english name is:Mcgrady
                 */
            }
        }
    }

    总结:
      1,委托声明使用关键字delegate,与方法相似有返回值和签名,但没有方法体。
      2,实例化委托对象时可以使用new运算符,也可以使用快捷语法,通常快捷语法更常用。
      3,供委托对象包装的方法返回类型和签名必须与委托匹配。
      4,调用委托与调用方法相似,但调用前最好判断委托对象的方法调用列表是否为空。

    二,委托揭秘

      先看一下编译器生成的代码,如图:

    从这张图中我们可以得到如下信息:
    1,委托的确是生成了一个类,同时也对文章开头的观点进行了验证。当编译器看到internal delegate void PrintMyName(string s);这行代码的时候就会自动定义一个完整的类,它包含四个方法,一个构造器,Invoke,BeginInvoke,EndInvoke。代码如下:

    internal class PrintMyName : System.MulticastDelegate
        { 
            //构造器
            public PrintMyName(Object obj,IntPtr method);
    
            //这个方法和源代码指定的原型一样
            public virtual void Invoke(string s);
    
            public virtual IAsyncResult BeginInvoke(string s,AsyncCallback callback,Object obj);
            public virtual void EndInvoke(IAsyncResult result);
        }

    2,所有的委托都派生自System.MulticastDelegate类,而System.MulticastDelegate又派生自System.Delegate类。
      其中MulticastDelegate中定义了三个非公共字段,分别是_target,_methodPtr,_invocationList。Delegate类中定义了两个只读的公共实例属性,分别是Target和Method。Target属性返回保存在MulticastDelegate中字段_target的值;Method属性返回保存在MulticastDelegate中字段_methodPtr的值。下面是实际应用的例子。

    public class Program
        {
            static void Main(string[] args)
            {
                Test t = new Test();
    
                PrintMyName pmn;
                pmn = new PrintMyName(Test.PrintChineseName);//或用快捷语法:PrintMyName pmn = Test.PrintChineseName
                Console.WriteLine("Target:{0},Method:{1}",pmn.Target,pmn.Method);
    
                pmn = t.PrintEnglishName;
                Console.WriteLine("Target:{0},Method:{1}", pmn.Target, pmn.Method);
    
                Console.ReadKey();
            }
        }

     程序输出结果:

    注意:如果委托对象包装的是一个静态方法,Target将返回Null值,如果是实例方法,那么Target的值就是回调方法要操作的对象Test。

    三,委托链

      委托链是由委托对象构成的一个集合。利用委托链,可调用集合中的委托所包装的全部方法。对上面的例子加以修改来演示委托链,代码如下:

    namespace DelegateDemo1
    {
        internal delegate void PrintFunction(string s);
    
        public class Test
        {
            public static void PrintFunction1(string name)
            {
                Console.WriteLine("This is static method PrintFunction1,create by {0}", name);  
            }
    
            public void PrintFunction2(string name)
            {
                Console.WriteLine("This is instance method PrintFunction2,create by {0}", name);
            }
    
            public void PrintFunction3(string name)
            {
                Console.WriteLine("This is instance method PrintFunction3,create by {0}", name);
            }
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                Test t = new Test();
    
                PrintFunction pmn1 = new PrintFunction(Test.PrintFunction1);
                PrintFunction pmn2 = t.PrintFunction2;
                PrintFunction pmn3 = t.PrintFunction3;
                PrintFunction pmnChain = null;//定义变量引用委托链
    
                pmnChain = (PrintFunction)Delegate.Combine(pmnChain, pmn1);//调用Delegate类的静态方法Combine添加委托到委托链
                pmnChain = (PrintFunction)Delegate.Combine(pmnChain, pmn2);
                pmnChain = (PrintFunction)Delegate.Combine(pmnChain, pmn3);
                pmnChain("Mcgrady");//调用委托链
                Console.WriteLine();
    
                pmnChain = (PrintFunction)Delegate.Remove(pmnChain, pmn2);//调用调用Delegate类的静态方法Remove从委托链中移除委托
                Console.WriteLine("*****The result as below after remove method of PrintFunction2*****");
                pmnChain("Mcgrady");
    
                Console.ReadKey();
            }
        }
    }

    程序输出结果:

    总结:
      1,构造委托链时,会调用Delegate类的两个静态方法Combine和Remove。C#的编译器自动为委托的实例重载了+=和-=操作符,这两个操作符分别调用Combine和Remove方法,所以我们的代码可以简化,如下面的代码。

    public class Program
        {
            static void Main(string[] args)
            {
                Test t = new Test();
    
                PrintFunction pfChain = null;
                pfChain += Test.PrintFunction1;
                pfChain += t.PrintFunction2;
                pfChain += t.PrintFunction3;
                pfChain("Mcgrady");
    
                Console.WriteLine();
                Console.WriteLine("*****The result as below after remove method of PrintFunction2*****");
    
                pfChain -= t.PrintFunction2;
                pfChain("Mcgrady");
    
                Console.ReadKey();
            }
        }

    输出结果与使用Delegate的静态方法Combine和Remove一样,这是因为编译器自动用Delegate类的Combine和Remove静态方法分别代替了+=和-=操作符(这一点可以从编译器生成的IL代码中得到验证)。

      2,当构造委托链时,_invocationList字段会被初始化为引用一个委托对象的数组,这一点也非常重要。

  • 相关阅读:
    FTP 协议和 HTTP 协议的比较
    HttpURLConnection的post请求,什么时候发出,writeData存在什么地方
    装饰器
    函数参数以及名称空间作用域
    函数的调用
    函数的返回值
    定义函数的三种方式
    函数
    day05
    day04
  • 原文地址:https://www.cnblogs.com/mcgrady/p/2568780.html
Copyright © 2011-2022 走看看