学习委托(3)-----解析委托的实现机制续篇

转载 2007年09月19日 10:43:00

来园子看看自己的博客,至今方体会到白驹过隙的含义,瞅着晾在这里的那几篇可怜的蝇头小文,却已然为时间的见证了。想想以前立下的壮志雄心,要做到学习不止,笔端也要勤耕耘,可现今自己博客的境况表现,哈哈!自己确实该怀揣点羞耻之心了,尽管可以找一大堆搪塞的理由。所以今天来涂鸦一篇小文,以慰吾心!

本文乃是《学习委托(3)-----解析委托的实现机制》的延伸或扩充吧,前篇未曾详谈到的关于多播委托的内容在此和各位初学的朋友一起学习一番,当然本人水平有限,高手若有幸光临,尽可拍砖便是。进入正题,同样,为便于讨论,把前篇文章中的简单代码稍加修改写在此处:

    class Program
    
{  
        
static void Main(string
[] args)
        
{
            goodfriend.middleSay allSay
=null
;
            allSay 
+= new
 goodfriend.middleSay(liyufeng.mySay);
            allSay 
+= new
 goodfriend.middleSay(liyufeng.mySecondSay);
            goodfriend.friendSay(allSay);
            allSay 
-= new
 goodfriend.middleSay(liyufeng.mySecondSay);
            goodfriend.friendSay(allSay);
            System.Console.Read();
        }

    }

    
class liyufeng
    
{
        
public static void
 mySay()
        
{
            System.Console.WriteLine(
"I Like You"
);
        }

        
public static void mySecondSay()
        
{
            System.Console.WriteLine(
"Love for ever"
);
        }


    }

    
class goodfriend
    
{
        
public delegate void
 middleSay();
        
public static void
 friendSay(middleSay say)
        
{           
            say();
        }

    }

前面的文章中已经谈到了多播委托,即一个委托对象可以挂接多个方法,如下面的代码:

            goodfriend.middleSay allSay=null;
            allSay 
+= new
 goodfriend.middleSay(liyufeng.mySay);
            allSay 
+= new goodfriend.middleSay(liyufeng.mySecondSay);

嘿嘿!看到上面第一句代码可能有初学的朋友会有疑问喽,goodfriend里的midlleSay委托类型并不是其静态成员,那为什么此处可以直接通过类名的方式进行访问呢?不要被public delegate void middleSay()这句代码的表面现象迷惑了,以为这里仅是声明了一个普通的类型,其实通过这句,编译器会为我们声明一个继承自System.MulticastDelegate的类,那么就是说,委托类型middleSay是goodfriend的一个嵌套类(有的文章也叫内部类),这再一次证明了在前面的文章中所介绍到过的委 托在.NET中是一个类;明白了这点,上面的疑问也就可以消散了。紧接着的两句代码实现了多播委托的挂接,运算符“+=”在此处功劳可不小,先从表面上认 识一下,这样理解应该可以:把运算符右侧用来包装方法的委托对象添加到左侧的委托对象中,并且再赋给左侧的委托对象;此处就是allSay了。所以通过这里两行代码,方法mySay和mySecondSay都已经挂接到委托allSay上了有过C++编程经验的朋友对这里“+=”的用法应该很熟悉,心中肯定会想这是所谓的运算符重载;其实这里的“+=”只不过是为了语法简洁,C#编译器演绎的一个把戏而已,并不是实在的运算符重载(当然,C#是支持运算符重载的,有兴趣的朋友可以通过Ildasm工具看一下Delegate或是MulticastDelegate类,其中并未有对“+=”的重载);编译器在这里会直接把“+=”转化为对静态方法Combine的调用,下面通过反编译工具Reflector(用微软自带的Ildasm也可以)得到的allSay += new goodfriend.middleSay(liyufeng.mySay)的IL代码(为便于理解此处省略掉了一些代码):

 L_0010: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)

从IL代码可以看出确实是对Combine方法进行了调用,有朋友可能又要问了,前面提到我们的委托类型middleSay是继承自System.MulticastDelegate,为何此处调用的是System.DelegateCombine 方法;这点前篇文章中谈到过,MulticastDelegate是继承自Delegate的,而在Delegate类中并未对Combine方法进行重 写,所以调用父类Delegate或是子类MulticastDelegate的Combine方法,效果是一样的。我们再来看一下Combine方法都 做了些什么,直接看通过得到的源代码了:

public static Delegate Combine(Delegate a, Delegate b)
{
    
if (a == null
)
    
{
        
return b;//直接返回委托对象,上面我们挂接第一个方法的时候,到这里应该就返回了

    }

    
if (b == null)
    
{
        
return
 a;
    }

    
if (!InternalEqualTypes(a, b))
    
{
        
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"
));
    }

    
return a.CombineImpl(b); //挂接两个或两个以上方法的时候,调用这里的CombineImpl方法
}


这里关键就是CombineImpl方法了,它是一个虚方法,在MulticastDelegate类中进行了重新定义,所以到最后我们调用的是MulticastDelegate类中的CombineImpl方法,在CombineImpl方法中才真正实现了委托方法链的挂接操作。同样我们可以方便的用“-=”操作符从委托方法链中移除一个方法,正如allSay -= new goodfriend.middleSay(liyufeng.mySecondSay)所示;“+=”操作符类似,C#编译器会把“-=”转化为对静态法Remove的调用。当然,我们可以在代码中直接调用Combine和Remove方法,而不用C#编译器提供的操作符,来实现对委托方法链的操作

 

通过上面的分析,我们可以了解到,作为基类的Delegate提供了CombineImpl等一系列的虚方法,从而子类型可以重新实现这些虚方法,只是到目前为止,微软并未从再从Delegate类继承一个独立的类型来支持单播委托,想是微软开发小组认为没有必要,而是单播与多播委托都由同一个类MulticastDelegate来支持了,如果样的话,感觉MulticastDelegate类和Delegate类完全可以合并为一个类了;当然也需微软的工程师牛人们以可能会研发出特殊功能的委托类型,从而保留Delegate类以便扩展。

 

   最 后再看一下关于委托方法的调用say(),很简单的一行代码,在前一篇文章中已经对委托方法的调用进行了分析,得出此处会转化为对Invoke方法的调 用,需要明白的是Invoke方法并非Delegate或是MulticastDelegate中的方法,而是编译器自动生成的。如下IL代码:


.method public hidebysig newslot virtual instance void Invoke() runtime managed
{
}


我们看出Invoke方法是一个空方法,没有任何的实现代码,那么这个空方法却又如何能对委托的方法进行调用通过Reflector得到Invoke的C#代码如下:

[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]
public virtual void
 Invoke();

我们看到,Invoke方法上贴了一个MethodImpl特性(即Attribute),通过MethodImpl特性标记Invoke方法的实现由CLR运行时提供(MethodCodeType.Runtime),至于CLR为Invoke方法提供的实现代码,以本人的水平是无从探究了,不过其中用到反射技术应该是毋庸置疑的。其实查看一下Delegate类中的方法,发现有DynamicInvokeDynamicInvokeImpl方法其中在DynamicInvoke中对DynamicInvokeImpl进行了调用,而在DynamicInvokeImpl方法中则是运用.NET的反射技术对委托方法进行了调用,所以我们可以猜想一下,CLR运时应该是把对Invoke方法的调用桥接为对DynamicInvoke的调用

   OK!讨论到这里就要结束了,本文是把自己对委托的理解记录于此,希望对委托不甚了解的朋友对.NET中的这项技术有一个比较好的认识,但本文探讨的毕竟是理论,而委托在实践中应用也颇为广泛,所以运用委托解决实际问题能力,还是要靠各自日常的修炼了。

另外附上本文VB.NET代码实例,方便学习VB.NET的朋友们对照:

 

c#委托几篇好文章 续篇

大白话系列之C#委托与事件讲解(三) [我希望大家在看完文章的时候,多做做练习,自己也可以想个场景,动动手,这样才会有深刻的印象,不然遗忘的速度非常快] 今天我接着上面的3...

C++实现的委托机制(3)

C++实现的委托机制(3) 1.引言 按上一篇文章的方法,你已经可以使用任意参数的函数委托了。这里介绍下MyGUI实现的两个辅助类,CDelegate类和IDelegateUnlink。如果你...
  • gouki04
  • gouki04
  • 2011年10月08日 15:16
  • 2032

C++学习笔记 5th —— 万能流程控制if语句 续篇:扩展与实际应用

.Hey, guys, I am back.额呵···好吧,这一篇是C++学习笔记 5st —— 万能流程控制if语句 续篇:实际应用OK,那么我们开始吧。······ ······ ······...

Scrapy 入门学习笔记(3) -- 使用 Item 类转换传输数据以及ItemLoader 机制解析

最近学习用 Scrapy 框架写爬虫,简单来说爬虫就是从网上抓取网页,解析网页,然后进行数据的存储与分析,将从网页的解析到数据的转换存储。将学习过程中用到的解析技术,Scrapy 的各个模块使用与进阶...
  • Ahri_J
  • Ahri_J
  • 2017年05月18日 14:02
  • 1118

优雅设计封装基于Okhttp3的网络框架(三):多线程下载功能核心实现 及 线程池、队列机制、终止线程解析

通过前两篇博文的学习,已经带领大家学习了HTTP协议与Okhttp相关内容的学习,并且在上篇博文已经完成了初始编码工作:定义好了网络请求接口DownloadCallback 和网络请求类HttpMan...

OSChina客户端源码学习(3)--轮询机制的实现

主要以OSChina Android客户端源码中Notice的轮询机制进行解读。一、基础知识 一般IM(即使通讯)的实现有两种方式:推送和轮询,推送就是服务器主动向客户端发送消息,用特定的协议比如XM...
  • csp277
  • csp277
  • 2015年07月20日 16:49
  • 827

C#委托机制源代码

  • 2014年03月25日 19:15
  • 502B
  • 下载

java ClassLoader类解析-双亲委托机制

做Java开发,对于ClassLoader的机制是必须要熟悉的基础知识,本文针对Java ClassLoader的机制做一个简要的总结。因为不同的JVM的实现不同,本文所描述的内容均只限于Hotspo...

IOS委托机制

  • 2012年12月28日 11:25
  • 26KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:学习委托(3)-----解析委托的实现机制续篇
举报原因:
原因补充:

(最多只允许输入30个字)