[CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系
原文:[CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系
CLR要求所有类型最终都要从System.Object派生。也就是所,下面的两个定义是完全相同的,
//隐式派生自System.Object
class Employee {
.....
}
//显示派生子 System.Object
class Employee : System.Object {
.....
}
由于所有类型最终都是从System.Object派生的,所以可以保证每个类型的每个对象都有一组最基本的方法。
System.Object提供了如下所示的公共实例方法。
| Equals(Object) | 确定指定的对象是否等于当前对象。如果两个对象具有相同值就返回ture. |
| GetHashCode | 返回对象的值得一个哈希码。如果某个类型的对象要在哈希表集合中作为key使用,该类型应该重写这个方法。方法应该为不同的对象提供一个良好的分布。 |
| ToString | 该方法默认返回类型的完整名称(this.GetType().FullName)。 |
| GetType | 返回从Type派生的一个对象的实例,指出调用GetType的那个对象是什么类型。返回的Type类型可以与反射类配合使用,从而获取与对象的类型相关的元数据信息。 |
System.Object的受保护方法
| MemberwiseClone | 这个非虚方法能创建类型的一个新实例,并将对象的实例字段设为与this对象的实例字段完全一致。返回的是对新实例的一个引用 |
| Finalize | 在垃圾回收器判断对象应该被作为垃圾收集之后,在对象的内存被实际回收之前,会调用这个虚方法。需要在回收之前执行一些清理工作的类型应该重写这个方法。 |
CLR要求所有对象都是用new操作符来创建。比如
Employee e = new Employee("ConstructorParam1");
以下是new操作符所做的事情:
1)它计算类型及其所有基类型(一直到System.Object)中定义的所有实例需要的字节数。堆上的每个对象都需要一些额外的开销成员——"类型对象指针(type object pointer)"和"同步块索引"(sync block index)。这些成员由CLR用于管理对象。这些额外成员的字节数会计入对象大小。
2)它从托管堆中分配指定类型要求的字节数,从而分配对象的内存,分配的所有字节都设为零(0)。
3)它初始化对象的"类型对象指针"和"同步块索引"成员。
4)调用类型的实例构造器,向其传入对new的调用中指定的任何实参(本例中是"ConstructorParam1")。大多数编译器都在构造器中自动生成代码来调用一个基类的构造器。每个类型的构造器在被调用时,都要负责初始化这个类型定义的实例字段。最后调用的是System.Object的构造器,该构造器只是简单的返回,不会做其它任何事情。
new 执行了所有的操作后,会返回执行新建对象的一个引用。在本例中,这个引用会保存到变量e中,具有Employee类型。
注意:上面提到过"类型对象指针",类型对象不是类型的对象/实例,这两者是有区别的。
----------------------------------------------------------------------------------
CLR最重要特性之一就是类型的安全性。在运行时,CLR始终知道一个对象的类型,可以调用GetType方法,得到对象的类型。
CLR允许将一个对象转换为它的实际类型或者它的任何基类型。
C#不要求使用特殊语法即可将一个对象转换为它的任何及类型,因为向基类型的转换被认为是一种安全的隐式转换。但是,将对象转换为它的某个派生类时,C#要求开发人员只能进行显示转换,因为这样的转换在运行时可能失败。
public static void Main() {
// 不需要转型
Object o = new Employee();
// 需要进行强制类型转换
Employee e = (Employee) o;
}
在C#语言中进行类型转换的另一种方式是使用is操作符。is操作符检查一个对象是否兼容指定的类型,并返回一个Boolean值(true和false)。注意,is操作符是不会返回异常信息的。
is操作符通常这样使用:
if ( o is Employe ){
Employee e = (Employee) o;
}
在这段代码中,CLR实际是会检查两次对象的类型。is操作符首先核实o是否兼容Employee类型。如果是,在if内部,CLR还会再次核实o是否引用一个Employee。CLR的类型检查增强的安全性,但无疑也会对性能造成一定影响。
C#专门提供了 as 操作符,目的就是简化这种代码的写法,同时提升性能。
as操作符通常这样使用:
Employee e = o as Employee;
if ( e != null ){
//在if中使用e
}
命名空间和程序集不一定是相关的,也就是说它们之间没有必然联系。
----------------------------------------------------------------------------------
现在将解释类型、对象、线程栈和托管堆在运行时的相互联系。此外,还将解释调用静态方法、实例方法和虚方法的区别。
我们先从线程栈开始。
1. 图4-2展示了已加载了CLR的一个Windows进程。在这个进程中,可能存在多个线程。一个线程创建时,会分配到一个1MB大小的栈。这个栈的空间用于向方法传递实参,并用于方法内部定义的局部变量。图4-2展示了一个线程的栈内存(右侧)。栈是从高地址向低地址构建的。在图中,线程已执行了一些代码,现在,假定线程开始执行的代码要调用M1方法了。

2. 在一个最基本的方法中,会有一些"序幕"代码,负责在方法开始时做它工作之前对其进行初始化。另外,还包括了"尾声"代码,负责在方法完成工作之后对其进行清理,然后才返回至调用者。M1方法开始执行时,它的"序幕"代码就会在线程栈上分配局部变量name的内存,如图4-3所示。

3. 然后,M1调用M2的方法,将局部变量name作为一个实参来传递。这造成name局部变量中的地址被压入栈(参见图4-4)。在M2方法内部,将使用名为s的参数变量来标识栈位置(有的CPU架构会通过寄存器来传递实参,以提高性能)。另外,调用一个方法时,还会将一个"返回地址"压入栈中。被调用的方法在结束后,应该返回到这个位置(同样参见图4-4)。




internal class Employee {
public int32 GetYearsEmployed() { ... }
public virtual String GenProgressReport() { ... }
public static Employee Lookup(String name) { ... }
}
internal sealed class Manager : Employee {
public override String GenProgressReport() { ... }
}
2. 我们的Windows进程已启动,CLR已加载到其中,托管堆已初始化,而且已创建一个线程(连同它的1MB的栈空间)。该线程已执行了一些代码,现在马上就要调用M3的方法。图4-6展示了目前的状况。M3方法包含的代码演示了CLR是如何工作的。








GetType
[CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系的更多相关文章
- 【.Net基础一】 类型、对象、线程栈、托管堆运行时的相互关系
目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 J ...
- .Net 类型、对象、线程栈、托管堆运行时的相互关系
JIT(just in time)编译器 接下来的会讲到方法的调用,这里先讲下JIT编译器.以CLR书中的代码为例(手打...).以Main方法为例: static void Main(){ Cons ...
- [No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1
引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone其实也就是对象复制.复制又分为了浅度复 ...
- [No0000B9]C# 类型基础 值类型和引用类型 及其 对象复制 浅度复制vs深度复制 深入研究2
接上[No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1 对象复制 有的时候,创建一个对象可能会非常耗时,比如对象需要从远程数据库中获取数据来填充,又或者创建对象需要读取硬 ...
- C# (类型、对象、线程栈和托管堆)在运行时的相互关系
在介绍运行时的关系之前,先从一些计算机基础只是入手,如下图: 该图展示了已加载CLR的一个windows进程,该进程可能有多个线程,线程创建时会分配到1MB的栈空间.栈空间用于向方法传递实参,方法定义 ...
- Java基本类型的内存分配在栈还是堆
我们都知道在Java里面new出来的对象都是在堆上分配空间存储的,但是针对基本类型却有所区别,基本类型可以分配在栈上,也可以分配在堆上,这是为什么? 在这之前,我们先看下Java的基本类型8种分别是: ...
- 重温CLR(三)类型基础
所有类型都从System.Object派生 “运行时”要求每个类型最终都要从System.Object类型派生.也就是说,一下两个类型的定义完全一致. //隐式派生自Object class Empl ...
- NET CLR via C#(第4版)第4章 类型基础
本章内容: 1 所有类型都从System.Object派生 2 类型转换 3 命名空间和程序集 4 运行时的相互关系 本章讲述使用类型和CLR时需掌握的基础知识.具体地说,要讨论所有类型都具有的一 ...
- [Clr via C#读书笔记]Cp4类型基础
Cp4类型基础 Object类型 Object是所有类型的基类,有Equals,GetHashCode,ToString,GetType四个公共方法,其中GetHashCode,ToString可以o ...
随机推荐
- Android Tween Animation
View Animation, 它显示在view向上Tween Animation Tween动画.本质上没有变化View对象本身.只要改变它绘制 实施方式有两种.一个xml定义,直接在代码中的定义 ...
- 我有DIY一Android遥控-所有开源
我有DIY一Android遥控-所有开源 1.试用 记得宋宝华在「设备驱动开发具体解释」提出一个这种理论「软件和硬件互相渗透对方的领地」,这次证明还是确实是这样,使用上层APP软件加上简单的更为简单的 ...
- C#中的动态特性
众所周知,C#和Java一样,都是一门静态语言.在C# 4.0之前,想要和动态语言(诸如Python.Javascript等)进行方便地互操作是一件不太容易的事情.而C# 4.0为我们带来的dynam ...
- OpenCV视频播放方法
OpenCV视频播放方法 今天折腾了一下OpenCV的视频播放功能,希望能对项目又帮助- 代码还是非常easy的,仅仅是之前遇到点小麻烦,找不到cvCreateFileCapture函数的定义,花了一 ...
- Windows Phone 8 应用内截图
WriteableBitmap wb = new WriteableBitmap(this.LayoutRoot, new MatrixTransform()); //wb.Render(this.L ...
- gradle中使用cobertura做代码覆盖(转)
gradle很好用,但是默认是没有代码覆盖功能的,只好自己写.曾经在网上找到过别人的一段脚本,虽然也能用,但是有一些不爽的地方,一个原因是它不支持对层级工程中全部代码的覆盖,另一个原因是它用替换bui ...
- 82. NotesclientPrint相同的信息,以状态栏的问题
这可能是一个小问题.但其他人也应该得到满足.在Notesclient使用LotusScript的Print当该语句是输出到状态栏,假设实际参数传递多次调用相同,状态栏将显示只有一次的信息. 例如: P ...
- 原子操作(atomic operation)
深入分析Volatile的实现原理 引言 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共 ...
- Gradle增量学习建筑
请在本系列下面的文章下载Github演示示例代码: git clone https://github.com/davenkin/gradle-learning.git 假设我们Gradle ...
- 彩色图像--色彩空间 HSI(HSL)、HSV(HSB)
得知DIP文章66日 转载请注明文章出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管.欢迎共同开发:https://g ...