关于实例方法,在我头脑中也基本能理清了...只是不知对否,现将它说出来.
的确,过于死钻.net平台的细节并不是明智的学习方法.然而我觉得要编写出优秀,高效的托管代码,必须要对.net下的方法调用机制有个准确的理解.要明白当一个实例类的方法被调用时,在高级语言提供的简洁语法背后,CLR如何为我们找到该方法,并调用它...
请注意,我并非要探求CLR将实例方法处理成哪些数据格式,只是要清楚地理解它的原理...
一般来说,原理通常是简单的,而实现是复杂的.
比如就我目前所知的:
类型分为值类型和引用类型;
值类型的储存位置有两种情况:
1)当值类型作为类的成员被定义时,如int型,它将随着类的实例化而在托管堆上被分配空间.
2)当值类型作为方法的局部变量时,它将随着方法的调用被分配在线程的调用堆栈上,并随着方法的调用结束而自动pop出堆栈.
引用类型的储存位置也有两种情况:
1)当引用类型作为类 classA 的成员被定义时,存放实例引用的变量被分配在托管堆上classA的所属空间内,而它指向的实例对象则分配在托管堆上的另一处.所以当classA被GC回收后,其内部的引用也不存在了,同时该引用所指向的对象也因为成为不可达对象而在下一次GC运行时被GC回收.
2)当引用类型作为类的方法(不管是实例方法还是静态方法)内的局部引用变量实现时,该引用变量被分配在线程的调用堆栈上,而它所指向的实例则在托管堆上分配,同样,方法调用结束后,引用变量被弹出,它所指向的托管堆上的实例也成为不可达对象,将被GC回收.
类的方法被编译后,形成一串IL指令,当该方法第一次使用时,CLR将这些IL指令载入内存,并存放在程序区.可能在此之前,CLR已经提前将该类的方法表也载入内存了,方法表存放的是该类的每个方法的地址.如下:
--------------------
| method1--->地址1 |
| method2--->地址2 |
| method3--->地址3 |
-------------------
而在每个类实例(注意是实例)内部,都有一个类的附加成员(相对于值类型而言):一个方法表指针,它指向的是上图这个方法表在内存中的地址.(我们不考虑更细节的东西,否则会增加理解的难度,实际上也不用我们去理解,否则微软靠什么吃饭??^^,另外还有一个附加成员是一个同步块之类的东东吧,用于线程的同步的.)
这样当我们在程序中调用一个方法时,在运行时,CLR会根据该类的方法表指针找到方法表,然后在方法表中找到对应的方法的IL指令的存放地址,以此将IL指令加载到线程的调用堆栈中,同时还会在堆栈上为该方法内的值类型分配空间,为该方法内声明的引用类型分配内存(在托管堆上),
调用结束后,即堆栈用IL指令转换的操作数完成计算操作后,所有分配的变量(值类型或引用类型)都会被弹出.(引用类型变量所指向的实例会成为不可达对象,会被GC回收)
注意,我现在只是将IL指令作为讨论对象...实际上执行的还是JIT编译器编译的本地机器码.只是它更涉及到更复杂的操作,如本地机器码还有自己的调用堆栈..等...这就不是我的脑袋所能相通的了...所以不用去想它..我只要想通这样流程就可以对我写程序有针对性的指导了.