这章不好理解,我是连抄带找的。。。。。。。。。。。
一、System.Object
所有类型的基类,也就是所有类型从它派生。 公开了几个方法:
- Equals : 判定两个对象是否具有相同的值(相等性和同一性后续讨论 )
- GetHashCode
- ToString() 默认返回类型的完整名称,但是经常会重写返回表示对象状态的string。例如:int32 的toString() 显示字段的值
- GetType:返回从type派生的一个类型的实例,调用的对象是什么类型。
- MemberwsiseClone: 深度拷贝
- Finalize : 在垃圾回收器判定对象是垃圾后,在对象的内存被实际回收之前,调用这个虚方法。需要在回收内存之气那执行新区管理工作的类型应该重写该方法。顺便说下:Finalize是会先执行父类Finalize 如果父类Finalize执行后,子类再执行Finalize时候会引发异常。具体后面介绍。
二、new 关键字
Dog dog= new Dog(“旺财”);
作用
1、计算类型及其所有基类型(一直到 System.Object )中定义的所有实例字段所需要的字节数。对象含有额外字节开销,(1.类型对象指针 2.同步索引快 )CLR利用这些成员管理对象。额外成员的字节数要计入对象的大小
2、从托管堆中分配类型要求的字节数,从而分配对象的内存,分配的所有字节都设为零(0)
3、初始化对象的类型指针和同步索引成员
4、调用类型的实例构造器,传递在new调用中指定的实参(eg:旺财)编译器都在构造器中自动生成代码来调用基类构造器 ,每个类型的构造器否负责初始化该类型定义的实例字段,最终调用System.Object的构造器。因为没有定义任何实例字段所以该构造器什么都不做,直接返回。
三、类型转换 is 和 as
1、类型转化规则: 子类可以直转为基类 public class Dog:Anmial{}
狗是动物, Anmial animal=new Dog();
基类不能直接转化为子类
Dog dog=new Animal(); (X) 可以这么理解编译器要做类型推断,但是呢动物不一定是狗还可能是人或者别的动物猫。另外,语法安全上也是要求不能直接从基类转为子类。
2、is 和as
类型需要转化通常我们会这样: 因此各自利弊自己选择
void GetName(Anmial animal)
{
If(animal is Dog){ Dog dog=(Dog)animal ; xxx} //此处会做两次的类型检查 ,失败返回false
}
void GetName(Anmial animal)
{
Dog dog=animal as Dog; //此处会做一次次的类型检查 ,失败返回 null
If(dog !=nul){ xxx }
}
四、运行时的相互关系
- 简单方法的调用需要预先知道的术语:
A:进程、线程(后面章节详细介绍)
B:线程栈:CLR创建线程时候会分配1M的栈空间,特点是从高位向地位地址构建(这里有个原因大家想想为啥呢,为啥不是从地位向高位构建)
C:压栈:方法的参数的地址将会以该形式进入线程栈
序幕代码(开场):我也很无奈为啥翻译成这个名字, prologue
开始执行方法前初始化变量,在栈上分配内存
尾声代码(后记):我也很无奈为啥翻译成这个名字, epilogue
执行完后清理内存并且返回到调用者。
程序执行开始后,加载一个CLR的一个windows进程,进程有多个线程。线程创建时候会分配到1M的栈,栈空间用于向方法传递实参,实参,实参,方法内部定义的局部变量也在栈上,栈上已经有了一些数据了(顶部阴影)。现在执行到了M3的方法。
执行M3时候,CLR会初始化M3的方法,包括开场代码和结束代码,开场代码用来初始化局部变量并且分配内存,引用类型指向null,值类型设置为0.
1.运行时-值类型的分配和方法调用
图中3的返回地址是在M3中调用执行M2的时候,压栈进去的,目的是告诉调度指针执行完M2的时候记得返回这里,执行M2的时候类似的分配空间,执行完M2之后 4,5将会被结束代码(CLR叫做尾声代码,这什么鬼翻译!)清除掉,专业术语叫做 栈针unwind行为,同时栈针指向3继续执行,当执行完M3时候同样重复清除,栈针指向调用M3的返回地址,(该地址在 线程栈0位置下方,1位置上方一点)这样就结束了方法调用。
有了这些基本知识接下来我们看下堆上的变化情况:先看下一段代码
同理要分配一个线程栈,调用方法前,有个序幕代码、和尾声代码。行为和上面讲到的一致。
- 运行时-引用类型和方法的调用
此处我们更关注堆上的变化
当执行到 new的关键字作用:结算对象占用内存大小(对象的类型指针大小,同步索引快、局部变量、基类类型消耗的字段等一直计算到Object)之后上形成如图:
17行代码执行完毕后形成如下图示:结合代码部分注释和下图理解运行时和方法的调用。
Type类型对象:所有类型对象的基类,它的类型对象指针指向自己。
现在解释下 你方法中的的GetType() 时候自己没有定义为什么能调用到,开始我们讲了这个呢是基类的Object的非虚方法 GetType(),而且图中的Dog类型对象 Animal类型对象,都是Type的一个实例,因此就知道了你调用的对象的实际类型。(具体怎么知道?看下面解释)
类型本质上也是对象,CLR创建类型对象时,必须初始化以上类型在分配时包含的成员,初始化成什么呢?CLR开始在一个进程中运行时,会立即为强命名程序集MSCorLib.dll中定义的System.Type类型创建一个特殊的类型对象。
而我们自定义的用户类型都是该类型的“实例”。也就是对象指针指向 Type类型对象。
当我们调用GetType()是时候返回的是:存贮在特定类型对象上的成员的地址指针。
这个怎么理解呢就是你调用Dog.GetType()返回的是存贮在Type类型上的对象类型指针地址,
即Dog类型对象指针,返回了Dog类型对象指针自然就知道了Dog是什么类型了。
对象类型指针和同步索引快、字段空间、方法地址空间=套餐。以后以套餐相称