所有类型都从System.Object派生

“运行时”要求每个类型最终都要从System.Object类型派生。也就是说,一下两个类型的定义完全一致。

//隐式派生自Object
class Employee{


//显式派生自Object
class Employee: System.Object{
}

由于所有类型最终都从System.Object派生,所以每个类型的每个对象都保证了一组最基本的方法。具体地说,System.Object类提供了如表4-1所示的公共实例方法。

此外,从System.Object派生的类型能访问下表的受保护的方法

clr要求所有对象都用new操作符创建。以下代码展示了如何创建一个List对象:

ModuleItem modulePicc = new ModuleItem(“agent”);

以下是new操作符所做的事情。

1、 计算类型及其所有基类型(一直到System.Object,虽然它没有定义自己的实例字段)中定义的所有实例字段需要的字节数。堆上每个对象都需要一些额外的成员,包括“类型对象指针”(type object pointer)和“同步块索引”(synv block index)。clr利用这些成员管理对象。额外成员的字节数要计入对象大小。

2、 从托管堆中分配类型要求的字节数,从而分配对象的内存,分配的所有字节都设为零(0).

3、 初始化对象的“类型对象指针”和“同步块索引”成员。

4、 调用类型的实例构造器,传递在new调用中指定的实参(上例就是字符串“agent”)。大多数编译器都在构造器中自动生成代码来调用基类构造器。每个类型的构造器都负责初始化该类型定义的实例字段。最终调用System.Object的构造器,该构造器什么都不做,简单地返回。

new执行了所有这些操作之后,返回指向新建对象一个引用(指针)。在前面的实例代码中,该引用保存到变量modulePicc中,后者具有ModuleItem类型

顺便说一句,没有和new操作符操作符对应的delete操作符;换而言之,没有办法显式释放对象分配的内存。clr采用了垃圾回收机制,能自动检测到一个对象不再被使用或访问,并自动释放对象的内存。


备注:同步块索引是什么?

是索引啦。。。。不过这个索引指向哪儿,作用是什么,才是最关键的问题。引用类型对象的同步块索引有这么两个作用:

1. lock当一个线程lock的时候(即 Monitor.Enter ),该线程会检查参数中的对象的同步块索引, 是否已经有关联的同步块。若没有, CLR就会在全局的SyncBlock数组里找到一个空闲的项,然后将数组的索引赋值给该对象的同步块索引。若存在,则通过同步块索引获取SyncBlock数组的项。然后, 该线程会设置SyncBlock里的内容,标识出已经有一个线程占用了。当有其他线程想lock时,会检查参数的SyncBlock里的内容,发现已经有线程占用了,其他线程就会等待。lock执行完,占用的线程就会释放SyncBlock,其他线程就可以使用了。

2. GetHashCode(要说明下同步块索引在32位机器占用32位,高6位作为控制位,表示参与的操作,后26位的含义随着高6位的不同而不同。)GetHashCode时,先通过 ComputeHashCode 方法得出一个哈希值,然后将这个哈希值与其他几个值进行几次或操作,就得到了一个对象的GetHashCode方法返回给我们使用的值。其中参与或操作的一个值,就是同步块索引的后26位的值。而这时同步块索引的高6位代表的含义中就有表示这后26位值用于参加GetHashCode的含义。尽我能力写了,不知道能看明白不。附上链接,里面讲得很详细。

http://www.cnblogs.com/yuyijq/archive/2009/03/13/1410071.html


类型转换

clr最重要的特性之一就是类型安全。在运行时,clr总是知道对象的类型是什么。调用getType方法即可知道对象的确切类型。由于它是非虚方法,所以一个类型不可能伪装成另一个类型。例如,Employee类型类型不能重写GetType方法并返回一个SuperHero类型。

开发人员经常需要将对象从一种类型转换为另一种类型。clr允许将对象转换为它的(实际)类型或者它的任何基类型。每种编程语言都规定了开发人员具体如何进行这种类型转换操作。例如,c#不要求任何特殊语法即可将对象转换为它的任何基类型,因为向基类型的的转换被认为是一种安全的隐式转换。然后,将对阵转换为它的某个派生类型时,c#要求开发人员只能进行显式转换,因为这种转换可能在运行时失败。一下代码演示了向基类型和派生类型的的转换:

Object o=new Employee();

Employee e = (Employee)o;

使用C#的is和as操作符来转换类型

在c#语言进行类型转换的另一种方式是使用is和as操作符。这里比较常用的是as,因为会减少一次判断,毕竟这种转换还是有性能损失的。

is用法

Object o = new ObjectU;
Boolean bl = (o is Object); // bl 为 true.
Boolean b2 = (o is Employee); // b2 为 false.
如果对象引用null,is操作符总是返回false。

as用法

Employee e= o as Employee;
if(e!=null){
//在if语句中使用e
}

命名空间和程序集

命名空间对相关的类型进行逻辑分组,开发人员可通过命名空间方便地定位类型。例如,system.test命名空间定义了执行字符串处理的类型,而System.io命名空间定义了执行i/o操作的类型。

对于编译器,命名空间的作用就是为类型名称附加以句点分隔的符号,使名称变得更长,更可能具有唯一性。

重要提示:clr对“命名空间”一无所知。访问类型时,clr需要知道类型的完整名称(可能是相当长的、包含句点符号的名称)以及该类型的定义具体在哪个程序集中,这样“运行时”才能加载正确程序集,找到目标类型,并对其进行操作。

using指令简化了类型名称,编译器会对当前代码中未识别的类型自动去匹配using 中引用的命名空间,直到找到对应类型。当然也会有潜在问题:可能两个或更多类型在不同命名空间中同名。为了消除歧义,必须显式告诉编译器需要需要生死用哪个类型。

c# using指令的另一种形式允许为类型或命名空间创建别名。如果只想使用命名空间中的少量代码,不想它的所有类型都跑出来“污染”全局命名空间,别名就显得十分方便。

using a=wwwwww.asdasd;

注意:命名空间和程序集不一定相关。特别是,同一个命名空间中的类型可能在不同程序集中实现。同一个程序集也可能包含不同命名空间中的类型,例如system.int32和system.text.stringBuilder类型都在MSCorLib.dll程序集中。

运行时的相互关系

本节将解释类型、对象、线程栈和托管堆在运行时的相互关系。此外,还将解释调用静态方法、实例方法和虚方法的区别。

图4-2展示了已加载clr的一个windows进程。该进程可能有多个线程。线程创建时会分配到1mb的栈。栈空间用于向方法传递实参,方法内部定义的局部变量也在栈上。栈从高位内存地址向低位内存地址构建,图中现在已执行了一些代码,栈上已有一些数据了(栈顶阴影区域)。现在,假定线程执行的代码要调用M1方法。

最简单的方法包含“序幕”(prologue)代码,在方法开始做工作前对其进行初始化;还包含“尾声”(epilogue)代码,在方法做完工作后对其进行清理,以便返回至调用者。M1方法开始执行时,它的“序幕”代码在线程栈上分配局部变量name的内存,如下图。

  然后M1调用M2方法,将局部变量name作为实参传递。这造成name局部变量中的地址被压入栈,如图4-4。M2方法内部使用参数变量s标识栈位置(注意:有的cpu架构用寄存器传递实参以提升性能,但这个区别对于当前问题不重要)。另外,调用方法时还会将“返回地址”压入栈,被调用的方法在结束之后应返回至该位置。

M2方法开始执行时,它的“序幕”代码在线程栈中为局部变量length和tally分配内存,如图4-5。然后M2方法内部代码开始执行,最终,M2抵达它的return语句,造成CPU的指令执政被设置成栈中的返回地址,M2的栈展开(unwind)。之后,M1继续执行M2调用之后的代码,M1的栈帧将准确反应M1需要的状态。

最终,M1会返回到它的调用者。

现在,让我们围绕CLR来调整一下讨论,假如有以下两个类定义:

windows进程已启动,clr已加载到其中,托管堆已初始化,而且已创建一个线程(连同它的1mb栈空间)。线程已执行了一些代码,马上就要调用M3方法。图4-6展示了目前的状态。

jit编译器将M3的IL代码转换成本机CPU指令时,会注意到M3内部引用的所有类型,这时CLR要确认定义了这些类型的所有程序集都已加载。然后,利用程序集的元数据,clr提取与这些类型有关的信息,创建一些数据结构来表示类型本身。

稍微讨论一下这些类型对象。堆上的所有对象都包含两个额外成员:类型对象指针(type object pointer)和同步块索引(sync block index)。定义类型时,可在类型内部定义静态数据字段。为这些静态数据字段提供支援的字节在类型对象中分配。每个类型对象最后都包含一个方法表。在方法表中,类型定义的每个方法都有对应的记录项。

当CLR确认方法需要的所有类型对象都已创建,M3的代码 编译之后,就允许线程执行M3的本机代码。M3的“序幕”代码执行时必须在线程栈中为局部变量分配内存,如图4-8所示。顺便说一句,作为“序幕”代码的一部分,clr自动将所有局部变量初始化为null或者0.然而,如果代码视图访问尚未显式初始化的局部变量,c#会报告错误,使用了未赋值的局部变量。

此外,在调用类型的构造器(本质上是可能修改某些实例数据字段的方法)之前,clr会先初始化同步块索引,并将对象的所有实例字段设为null或者0。new 操作符返回对象的内存地址,该地址保存在变量e中(变量e在线程栈上)。

  M3的下一行代码调用Employee的静态方法Lookup。调用静态方法时,clr会定位与定义静态方法的类型对应的类型对象。然后,jit编译器在类型对象的方法表中查找与被调用方法对应的记录项,对方法进行jit编译(如果需要的话),再调用Jit编译好的代码。由于lookyo方法在堆上构造一个新的manager对象,用joe的信息初始化它,返回该对象的地址。该地址保存到局部变量e中。这个操作结果如图4-10。

注意,e不再引用第一个manager对象。事实上,由于没有变量引用该对象。所以他是未来垃圾回收的主要目标。

M3的下一行代码调用Employee的非虚实例方法GetYearsEmployed。调用非虚实例方法时,jit编译器会找到与“发出调用的那个变量e的类型”对应的类型对象Employee。如果Employee类型没有定义正在正在调用的那个方法,jit编译器会回溯类层次结构(一直回溯到object,这也是方法覆盖的实现原理),并在沿途的每个类型中查找该方法。之所以能这样回溯,因为每个类型对象都有一个字段引用了它的基类型,这个信息在图中没有显示。

M3调用Employee的虚实例方法GetProgressReport时,jit编译器要在方法中生成一些额外的的代码;方法每次调用都会执行这些代码。这些代码首先检查发出调用的变量,并跟随地址来到发出调用的对象。变量e当前引用的是代表“jor”的manager对象,所以会调用manager的GetProgressReport实现。

以上我们讨论了源代码、il和jit编译的代码之间的关系。还讨论了线程栈、实参、局部变量以及这些实参和变量如果引用托管堆上的对象。结束本章之前,我们一起探讨下clr内部发生的事情。

注意Employee和Manageer类型对象都包含“类型对象指针”成员。这是由于类型对象本质上也是对象。clr创建类型对象时,必须初始化这些成员。初始化成什么呢?clr开始在一个进程中运行时,会立即为MSCorLib.dll中定义的system.Type类型创建一个特殊的类型对象。Employee和Manageer类型对象都是该类型的“实例”。因此,他们的类型对象指针成员会初始化成对System.type类型对象的引用。如图4-13

当然,system.type类型对象本身也是对象,内部也有“类型对象指针”成员。这个指针指向什么?他指向它它而本身,因为system.type类型对象本身是一个类型对象的“实例”。顺便说一句,system.objicet的getType方法返回存储在指定对象的“类型对象指针”成员中的地址。也就是说,getType方法返回指向对象的类型对象的指针。这样就可判断系统中任何对象(包括类型对象本身)的真实类型。

重温CLR(三)类型基础的更多相关文章

  1. 重温CLR(五)类型和成员基础

    类型的各种成员 类型可以定义以下种类的成员 1 常量 常量是指出数据值恒定不变的符号.这种符号使代码更易阅读和维护.常量总与类型管理,不与类型的实例管理.常量逻辑上总是静态成员. 2 字段 字段表示只 ...

  2. [CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系

    原文:[CLR via C#]4. 类型基础及类型.对象.栈和堆运行时的相互联系 CLR要求所有类型最终都要从System.Object派生.也就是所,下面的两个定义是完全相同的, //隐式派生自Sy ...

  3. NET CLR via C#(第4版)第4章 类型基础

    本章内容: 1 所有类型都从System.Object派生 2 类型转换 3 命名空间和程序集 4 运行时的相互关系   本章讲述使用类型和CLR时需掌握的基础知识.具体地说,要讨论所有类型都具有的一 ...

  4. 《C#从现象到本质》读书笔记(三)第3章C#类型基础(下)

    <C#从现象到本质>读书笔记第3章C#类型基础(下) 常量以关键字const修饰.C#支持静态字段(类型字段)和实例字段. 无参属性的get方法不支持参数,而有参属性的get方法支持传入一 ...

  5. [Clr via C#读书笔记]Cp4类型基础

    Cp4类型基础 Object类型 Object是所有类型的基类,有Equals,GetHashCode,ToString,GetType四个公共方法,其中GetHashCode,ToString可以o ...

  6. C#学习笔记——面向对象、面向组件以及类型基础

    C#学习笔记——面向对象.面向组件以及类型基础 目录 一 面向对象与面向组件 二 基元类型与 new 操作 三 值类型与引用类型 四 类型转换 五 相等性与同一性 六 对象哈希码 一 面向对象与面向组 ...

  7. CLR via C# - 基础拾遗

    编译器开关设置 IL代码质量 JIT本地代码质量 /optimize- /debug-(默认设置) 未优化 优化 /optimize- /debug+(full/pdbonly) 未优化 未优化 /o ...

  8. CLR设计类型之接口

    写在前面的话:             写到这一节的时候,CLR设计类型就已经结束了,因为CLR要求的是有一定基础的人看的,所以我们不是从基础类型以及运算符开始的,文章从一开始就讲的是深入面向对象编程 ...

  9. [No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1

    引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone其实也就是对象复制.复制又分为了浅度复 ...

随机推荐

  1. python的垃圾回收机制 继承的顺序C3算法

    Python垃圾回收    -- 引用计数        -- Python为每个对象维护一个引用计数        -- 当引用计数为0的 代表这个对象为垃圾    -- 标记清除        - ...

  2. 函数:生成1-n的随机数组,

    方法很笨,不过可行: #include <stdio.h> /** 功能:获取一个1-n的随机数数组,这些随机数都互不相同 ** 入参:n-表示最大随机数: *randArray -用于储 ...

  3. Docker 容器内配置Tomcat manager 远程控制

    下载tomcat镜像  , docker run it docker exec -ti 容器ID /bin/bash 进入容器 apt-get update ,  apt-get install vi ...

  4. [转载]织梦DEDE多选项筛选_联动筛选功能的实现_二次开发

    织梦默认的列表页没有筛选功能,但有时候我们做产品列表页的时候,产品的字段比较多,很多人都需要用到筛选功能,这样可以让用户更方便的找到自己所需要的东西,实现这个联动筛选功能需要对织梦进行二次开发,下面就 ...

  5. android studio 更新Gradle版本方法

    在导入其他项目时,经常由于gradle版本不一致而导致不能编译 解决方法: 第一步: 按提示点击让它下载,其实目的并不是要它下载,因为这样速度会很慢,这样做只是为了让它在本地创建相应的目录结构 第二步 ...

  6. java 对类型的基本操作小结

    1.json 字符串转换成对象 SyncCarriageStatusDTO dto= JSON.parseObject(value,SyncCarriageStatusDTO.class); List ...

  7. spring mvc:复选框(多选)

    以user为例,user下有 username用户,password密码, address地址, receivePaper是否订阅, favotireFramework兴趣爱好, user.java ...

  8. Selenium with Python 010 - unittest 框架(又称PyUnit 框架)

    unittest进行python代码单元测试 calculator.py--被测试类 #!/usr/bin/env python # -*- coding: utf-8 -*- # 将要被测试的类 c ...

  9. LinuxMint Mate 安装Google拼音输入法(Fcitx)

    安装Google拼音输入法(Fcitx)$ sudo apt-get install fcitx fcitx-config-gtk fcitx-frontend-all fcitx-ui-classi ...

  10. 三十三 Python分布式爬虫打造搜索引擎Scrapy精讲—数据收集(Stats Collection)

    Scrapy提供了方便的收集数据的机制.数据以key/value方式存储,值大多是计数值. 该机制叫做数据收集器(Stats Collector),可以通过 Crawler API 的属性 stats ...