NULL

快乐的活着...【湘西,蓝蓝的天,清清的水...】

  博客园 :: 首页 :: 新随笔 ::  :: 订阅 订阅 :: 管理 ::
  40 随笔 :: 1 文章 :: 1193 评论 :: 43 引用

    最近,拜读了王涛的《你必须知道的.net》前一章节,感受颇多,如此复杂的原理,在王涛的谈笑之间,不知不觉已被王涛诱惑于无形,由对象的产生,讲到对象的继承,封装,再到多态,再引申到模式,简直到了出神入化地步,心里又是妒忌,又是窃喜...

    下面,我就“多态”的一些体验写下来,至于对象的又生到死的过程,这里就不讲了。

    生物界,生命形式多姿多彩,无奇不有,无处不体现着多态,生物界的多种生态形式,归根到底是因为生物具有一定的“遗传性”,也许你长得比你弟弟要矮些,但不一定是你弟弟吃的东西比你好,很有可能,是”遗传“在作怪,或者某个“长得高的变量基因”没有遗传给你,或许是你的访问权限不够,那也是听天又命的事情。那么程序的世界里会是什么样子呢?具体的实现机制又是怎样的呢?

    在此之前,我们得给程序世界的"多态性",定一个量,要不然就会太泛,当然,有部分概论是借鉴王涛先生的,请大家体谅。

    多态指,同一类事物,同一方法,能表现不同的形式,比如抽象方法覆写,方法重写等等,都是多态的表现,我们来看一个简单的例子。

例一

Code

 

代码实在太简单,我都不好意思讲,说实话,在我没有看《你必须知道的.net》之前,我一直都认为c.name等于“C_name”,而且深信不疑。

    还是先简单的介绍一下.net的堆和栈机制,栈一般存放“值类型”,当然包括指针pointer,而且栈内存又系统负责回收,Clr的Gc也不用管,栈主要保存一些方法的调用指令,以及一些局部变量。也就是说,程序代码中的所有方法都会被“压栈”,就好比我们经常吃的“柠檬口味的乐事”,都是一片一片,整整齐齐又上往下压的。

    “引用类型” 的对象分配比较有意思,先在栈中,分配一个指针空间,此时为null,然后再堆中任意开辟一块内存空间,保持实实在在的数据,然后把堆的地址赋值给栈中的空指针,所以我们要明白一点,”值类型“是和指针完全不同的两个概念,只是他俩都存在栈中而已。堆中的数据需要Gc负责回收,所以我们能少用“引用类型”就要少用,少装点箱子,少开点箱子。

    我们来看看“栈内存”中的真实生活

请大家注意左上角第一行数据“0012FFC4”,请大家记住他的位置是第一行top one,我们来单步调试一下,再看下一张图。

单步调试后的“0012FFC4”已经排在top two了,第一行的栈地址是“0012FFC0”,也就是0012FFC4减去4,所以“压栈“是真实的,我绝不会忽悠大家。


    大家对堆和栈有一个比较直观的认识后,我们来看看C,B类型对象的变量和方法的分配情况,因为A是抽象的,所以不能直接构造对象,所以不在讨论范围,通过调试,获得一张B和C类型对象的变量和方法的分布图

 

同名变量情况, 从上到下,也就是压栈


抽象和虚方法,非抽象和虚方法

 

从图中,我们可以看出,从上到下,从子类到父类的变量依次被初始化,而且子类中都继承和分配了父类的变量,如果方法是抽象的,那么子类直接拷贝父类抽象方法的签名,并根据子类不同的方法实现,来重写父类的抽象方法。

你必须知道的.net中,处理同名变量和抽象方法,有两个原则,一是”对象创建原则“,和“就近原则”,其实都可以归纳为"就近原则”,再联想一下这张图,还有什么不可以搞定呢?

 

总结

    继承,封装,多态等这些概念,只有类比到现实世界中的种种,才可能理解得更透彻,希望对大家有些帮助,欢迎大家讨论,指教...

 

补充和纠正

    晚上看到了大家的评语,很贴切,也很有建设性,谢谢。

就在刚刚,我和王涛先生聊了一些关于,virtual方法和abstract方法和普通方法的内存分布情况,谢谢王涛先生指正一个非常普通,但又容易犯错误的问题。

纠正1,非virtual方法和非abstract方法,在子类中都没有父类的拷贝。

纠正2, 只要是virtual方法和abstract方法,不管子类中有没有override父类的方法,子类中都有父类的virtual方法和abstract方法的拷贝。

 

posted on 2008-07-25 16:26 王孟军! 阅读(1259) 评论(31)  编辑 收藏 所属分类: .net clr

评论

#1楼  2008-07-25 16:29 uwebs [未注册用户]
顶了再看
  回复  引用    

#2楼  2008-07-25 16:31 游客 [未注册用户]
好好研究一下!
  回复  引用    

#3楼  2008-07-25 16:35 姜敏      
队长,你那两个栈的图是怎么得到的啊?
  回复  引用  查看    

#4楼  2008-07-25 16:38 生鱼片      
楼主《你必须知道的.net》作者是王涛
  回复  引用  查看    

#5楼 [楼主] 2008-07-25 16:41 王孟军!      
@生鱼片
谢谢,谢谢
非常抱歉,写了一下午,头实在是晕
还请涛哥见谅
  回复  引用  查看    

#6楼  2008-07-25 17:05 涵舍愚人      
好文 
  回复  引用  查看    

#7楼  2008-07-25 17:10 Eeyore      
name 是多态?怎么我觉得DoAnythings()才是呢?
我觉得例子有点那个...A a = new C();这样如何呢?
  回复  引用  查看    

#8楼  2008-07-25 17:43 红尘中迷茫      
考虑一个实际应用的场景。。。
一个自动建站的项目,如何更好的应用继承和多态?
  回复  引用  查看    

#9楼  2008-07-25 17:44 Cheey [未注册用户]
很细,不错
  回复  引用    

#10楼  2008-07-25 17:49 留恋星空      
如果上午看到了,下午就不至于那么狼狈了,呵呵.
  回复  引用  查看    

#11楼  2008-07-25 18:06 说不得      
呵呵,如果把变量c的名称改为变量a,估计有些人就不会上当了。
  回复  引用  查看    

#12楼  2008-07-25 18:10 Justin      
顶顶!
  回复  引用  查看    

#13楼  2008-07-25 19:10 留恋星空      
感觉还是不太明白为什么?
  回复  引用  查看    

#14楼  2008-07-25 19:27 小龙3      
请教一下:

A c = new C();

C c = new C();

  回复  引用  查看    

#15楼  2008-07-25 19:55 路西菲尔      
A c = new C(); c.name
ldfld int32 A::name

C c = new C(); c.name
ldfld int32 C::name

如此而已,整得field也有多态似的
  回复  引用  查看    

#16楼  2008-07-25 20:41 横刀天笑      
说实话,没怎么看明白最后那一张图
  回复  引用  查看    

#17楼 [楼主] 2008-07-25 21:23 王孟军!      
@红尘中迷茫
这位兄弟的问题太抽象了,呵呵

@说不得
呵呵,那没办法了

@留恋星空
上午得上班

@Justin
谢谢

@留恋星空
看看书,动手调试一下

@小龙3
没看到,这位兄弟的问题
你大概是想问他们的区别吧?
C c = new C();
这个就不用说了
A c = new C();
至于这个,也就是把子类对象的地址赋值个A类型的引用类型

@路西菲尔
呵呵,抱歉,变量当然没有多态,我只是想说明一下他们的初始化位置和先后顺序

@横刀天笑
说实话,这就是一张父,子类对象变量和方法的初始化顺序图形
  回复  引用  查看    

#18楼  2008-07-25 21:33 Eeyore      
标题和内容不符啊~
过分强调name了,hoho~
  回复  引用  查看    

#19楼  2008-07-25 21:41 Eeyore      
你参考一下这篇文章
http://www.cnblogs.com/zhenyulu/articles/36058.html
和你说明的OO,CLR原理是一样滴说~
  回复  引用  查看    

#20楼 [楼主] 2008-07-25 21:55 王孟军!      
@Eeyore
谢谢,我会仔细看看的
  回复  引用  查看    

#21楼  2008-07-25 22:04 留恋星空      
值类型存放在内存的堆栈中,引用类型存放在内存的堆中?
正确否?
等哈调试,现在平复下心情,刚有个QQ群,发的图片太恶心了,吃饭没多久,想吐来着。俄。
  回复  引用  查看    

#22楼 [楼主] 2008-07-25 22:06 王孟军!      
@留恋星空
这位兄弟,真有意思

  回复  引用  查看    

#23楼  2008-07-25 22:25 留恋星空      
呵呵。有啥意思哩?不明白。
  回复  引用  查看    

#24楼 [楼主] 2008-07-25 23:26 王孟军!      
抱歉,方法的内存分别情况,有误,请看补充和纠正
  回复  引用  查看    

#25楼  2008-07-25 23:42 Cat Chen      
其实public string name是在构造函数里面初始化的,允许你不写构造函数直接在后面写初始值,这纯粹是语法糖。构造函数一定是从基类开始调用的,然后逐层构造上来,因此最后执行的构造函数就是最顶层的类。
  回复  引用  查看    

#26楼  2008-07-26 07:17 Ivan-Yan      
《你必须知道的.net》网上Blog中的文章我已经拜读了,觉得不过瘾,刚买了一本~
学习lz这种总结归纳的精神,记录点滴所得~
  回复  引用  查看    

#27楼  2008-07-26 09:48 狼Robot      
学习
  回复  引用  查看    

#28楼  2008-07-26 16:22 Bēniaǒ      
学习过.冒个泡作为记号.
  回复  引用  查看    

#29楼  2008-07-26 19:56 sss0669      
希望版主 在解释下 为什么是A.name希望版主更形象写写出来对我们初学者没看懂

  回复  引用  查看    

#30楼 [楼主] 2008-07-27 12:25 王孟军!      
@Cat Chen
@Ivan-Yan
@狼Robot
@Bēniaǒ
@sss0669
谢谢,在王涛的帮助下,已经修正了原来的错误图片

  回复  引用  查看    

#31楼  2008-07-28 10:40 茂子      
加油!!!!
  回复  引用  查看    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-07-29 11:22 编辑过


相关链接: