学习委托(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的朋友们对照:

 

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

来CSDN看看自己的博客,至今方体会到白驹过隙的含义,瞅着晾在这里的那几篇可怜的蝇头小文,却已然成为时间的见证了。想想以前立下的壮志雄心,要做到学习不止,笔端也要勤耕耘,可现今自己博客的境况表现,哈哈...
  • liyufeng1983
  • liyufeng1983
  • 2007年08月27日 12:29
  • 584

深入理解C#委托及原理

一、委托 设想,如果我们写了一个厨师做菜方法用来做菜,里面有 拿菜、切菜、配菜、炒菜 四个环节,但编写此方法代码的人想让 配菜 这个环节让调用方法的人实现,换句话说,就是想在方法被调用时接收代码 作...
  • farmwang
  • farmwang
  • 2016年06月19日 09:17
  • 1665

java实现事件委托

事件委托 委托
  • shuciqi
  • shuciqi
  • 2014年05月13日 14:27
  • 2390

Qt学习之路(48): 自定义委托

Qt学习之路(48): 自定义委托 2010-01-27 20:36:34 标签:Qt C++ 学习 教程 QT教程 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出...
  • carrie0728
  • carrie0728
  • 2016年07月13日 16:08
  • 668

c# 委托的学习日记

                曾经对委托和事件非常头疼,好在现在以自己的方式入门了,就行以自己的方式把它过一遍,当作学习日记吧。      委托(delegate)是一种引用类型,我们在处理他的时候...
  • huomm
  • huomm
  • 2007年11月21日 20:47
  • 19177

C++实现的委托机制

转载 C++实现的委托机制 1.引言 下面的委托实现使用的MyGUI里面的委托实现,MyGUI是一款强大的GUI库,想理解更多的MyGUI信息,猛击这里http://mygui...
  • taoqilin
  • taoqilin
  • 2016年03月28日 15:05
  • 743

Kotlin学习之委托机制

委托模式可以很好的替代实现继承,kotlin本身支持需要零样板代码,一个类Derived 可以继承Base并委托它所有的public 方法到一个指定的类: interface Base { ...
  • u014134488
  • u014134488
  • 2016年04月11日 17:56
  • 3593

如何在C#中的委托实现

先说下我为什么会用委托。在最开始想用委托的时候,其实我是拒绝的。为什么?因为结构看起来会有点乱啊,只是写个方法名,然后在括号写几个参数,但是实现却不在当前的对象中,给别人看的时候很不容易找诶。并且你一...
  • u014545772
  • u014545772
  • 2015年08月17日 18:48
  • 569

设计模式之观察者模式与事件委托

观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自己更新自己。         观察者结构模式图:     ...
  • wangdan199112
  • wangdan199112
  • 2014年02月16日 21:36
  • 2285

委托与继承

委托比继承灵活,可以动态配置,不会造成子类级数增长,另外可以通过对象的合成来实现多种功能(Decorator) 继承则相对不灵活,一旦选择了子类后,不能动态配置 委托是黑盒重用(看不到父类方法...
  • jiafu1115
  • jiafu1115
  • 2011年08月20日 10:46
  • 1103
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:学习委托(3)-----解析委托的实现机制续篇
举报原因:
原因补充:

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