第四章 类型基础

这章不好理解,我是连抄带找的。。。。。。。。。。。

一、System.Object

  所有类型的基类,也就是所有类型从它派生。 公开了几个方法:

  1. Equals : 判定两个对象是否具有相同的值(相等性和同一性后续讨论
  2. GetHashCode
  3. ToString() 默认返回类型的完整名称,但是经常会重写返回表示对象状态的string。例如:int32 toString() 显示字段的值
  4. GetType:返回从type派生的一个类型的实例,调用的对象是什么类型。
  5. MemberwsiseClone: 深度拷贝
  6. 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

扫描二维码关注公众号,回复: 6047564 查看本文章

  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 }

}

四、运行时的相互关系

  1. 简单方法的调用需要预先知道的术语:

 A:进程、线程(后面章节详细介绍)

 B:线程栈:CLR创建线程时候会分配1M的栈空间,特点是从高位向地位地址构建(这里有个原因大家想想为啥呢,为啥不是从地位向高位构建)

 C:压栈:方法的参数的地址将会以该形式进入线程栈

序幕代码(开场):我也很无奈为啥翻译成这个名字, prologue

  开始执行方法前初始化变量,在栈上分配内存

尾声代码(后记):我也很无奈为啥翻译成这个名字, epilogue

 执行完后清理内存并且返回到调用者。

程序执行开始后,加载一个CLR的一个windows进程,进程有多个线程。线程创建时候会分配到1M的栈,栈空间用于向方法传递实参,实参,实参,方法内部定义的局部变量也在栈上,栈上已经有了一些数据了(顶部阴影)。现在执行到了M3的方法。

执行M3时候,CLR会初始化M3的方法,包括开场代码和结束代码,开场代码用来初始化局部变量并且分配内存,引用类型指向null,值类型设置为0.

1.运行时-值类型的分配和方法调用

图中3的返回地址是在M3中调用执行M2的时候,压栈进去的,目的是告诉调度指针执行完M2的时候记得返回这里,执行M2的时候类似的分配空间,执行完M2之后 45将会被结束代码(CLR叫做尾声代码,这什么鬼翻译!)清除掉,专业术语叫做 栈针unwind行为,同时栈针指向3继续执行,当执行完M3时候同样重复清除,栈针指向调用M3的返回地址,(该地址在 线程栈0位置下方,1位置上方一点)这样就结束了方法调用。

有了这些基本知识接下来我们看下堆上的变化情况:先看下一段代码

 

同理要分配一个线程栈,调用方法前,有个序幕代码、和尾声代码。行为和上面讲到的一致。

  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是什么类型了。

猜你喜欢

转载自www.cnblogs.com/LiMin/p/10785536.html