码迷,mamicode.com
首页 > 其他好文 > 详细

委托的内部机制

时间:2015-09-22 01:24:25      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:

     委托是一种定义方法签名的类型,是对方法的抽象、封装。与委托的签名(由返回类型和参数组成)匹配的任何可访问类和结构中的任何方法都可以分配给该委托,方法可以是静态方法,也可以是实例方法。将一个方法绑定到委托时,C#和CLR允许引用类型的协变性逆变性

协变性是指方法的返回类型可以派生自委托的返回类型。

逆变性是指委托的参数类型可以派生自方法的参数类型。

协变性和逆变性只能用于引用类型,不能用于值类类型或void。所以不能将下面的方法与委托绑定:

        delegate Object DelegateCallBack(FileStream s);
        public int CallBackMethod(Stream s)
        {
            //dosomthing
            return 1;
        }

  而下面的方法是可以进行绑定的:

        public string CallBackMethod(Stream s)
        {
            //dosomthing
            return "1";
        }

委托具有以下特点:

1.委托类似于C++函数指针,但它们是类型安全的。

2.委托允许将方法作为参数进行传递。

3.委托可以用于定义回调方法。

4.委托可以链接在一起;例如,可以对一个事件调用多个方法。

5.方法不必与委托签名完全匹配。

      通过反编译工具可以知道委托类型会被编译成一个类(一个sealed类,所以不允许从委托类型派生任何类型),并且这个类继承自System.MulticastDelegate类(所有委托类型都派生自该类型)。委托继承了MulticastDelegate类的字段、属性和方法,其中三个重要的对于理解委托及委托链的内部机制非常有帮助的是_target, _methodPtr, _invocationList。具体解释一下:

_target:当委托对象包装一个静态方法时,该值为null。包装实例方法时,该值是回调方法要做操的对象。

_methodPtr:一个内部的整数值,标示要回调的方法。

_invocationList:委托链时,引用一个委托数组,否则为null。

下面通过具体的代码,调试查看上述三个字段的实际赋值情况:

回调静态方法:

        public delegate void DelegateSample(string content);
        static void Main(string[] args)
        {            DelegateSample staticInstance = SendMessage;
            Console.ReadLine();
        }
        public static void SendMessage(string content)
        {
            Console.WriteLine(content);
        }

调试可以发现:技术分享

_target并不是为null,而是引用的方法信息,为什么呢?这个我暂时也不知道。但是Target属性的值为null,没有问题,这个属性分装了字段_target,回调静态方法时为null。

回调实例方法:

        static void Main(string[] args)
        {
            DelegateSample x = new Program().SendQQ;
            Console.ReadLine();
        }
        public void SendQQ(string content)
        {
            Console.WriteLine(content);
        }

调试查看委托实例x:

技术分享

委托链:

        稍微修改一下上面的例子,哎,这个例子改的我自己都快受不了了。

        static void Main(string[] args)
        {
            DelegateSample x = new Program().SendMessage;
            DelegateSample sample = new DelegateSample(new Program().SendQQ);
            sample += SendEmial;
            sample += new Program().SendMessage;
            DelegateSample s = SendEmial;//与 sample 链中SendEmial同一个对象
            sample.Invoke("通知");
            new Program().SendNotice("QQ通知:*****", x);
            Console.ReadLine();
        }

 

调试查看委托实例x:可以发现_invocationList被初始化为引用一个委托数组。

技术分享

委托实例x:技术分享

委托链数组的第三个元素与委托实例x相同,实际上这两个委托都是方法SendMessage的引用,所以截图中的数值相同也就不大惊小怪了,既然是同一个方法的引用,那么函数的入口地址就应该相同。

另外一点:在对委托经行+=操作时,当发现当前委托变量已经引用了委托对象时,会新构造一个委托对象,并对新对象进行初始化,最后设置委托变量引用新的委托对象,之前的委托对象以及_invocationList字段引用的委托数组将会被当做垃圾某个时刻回收,-=操作同样会新建委托对象。

 

委托的内部机制

标签:

原文地址:http://www.cnblogs.com/ashleyboy/p/4827752.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!