运行时要求每个类型都是从System.Object派生,若是没有显示的写明继承关系,最后都会默认的从System.Object来派生。System.Object提供了四个公用方法和两个收保护方法:编程
公共方法 | 方法说明 |
---|---|
Equals | 比较两个对象的值是否相等,相等返回true |
GetHashCode | 返回对象的哈希码,若是想让对象在字典中作键使用,应当重写此方法提供一个分布均匀的哈希码,此方法本应该设计在接口中 |
ToString | 默认返回类型的完整名称:this.GetType().F ullName()。VS调试器会自动调用改函数来显示对象的字符串表示。理论上此函数应该察觉到与调用线程的CutureInfo并采起相关行动 |
GetType | 返回从Type派生的一个类型实例,是非虚方法。目的是为了防止类型重写改方法,隐瞒其类型,从而破坏其安全性 |
MemberwishClone | 建立一个新实例,而且设置新的实例对象字段和当前对象相同的值,而后返回其引用 |
Finalize | 垃圾回收以前执行,若是须要处理某些资源能够重写此方法 |
- 计算类型以及其基类(直到system.object)的实例字段须要的字节数,以及堆上额外成员须要的字节数。额外成员包括 类型对象指针 和同步索引块 - 从托管堆中分配本身数从而分配对象内存 - 初始化 类型对象指针 和同步索引块 成员的值 - 调用构造函数。每个构造函数都负责初始化该类型定义的实例字段 - 返回一个引用安全
CLR 最重要的一个特性就是类型安全。觉得不论是什么变量,调用一下GetType就能够知道他的具体类型,此方法是非虚方法,不能够进行假装和重写。而同时C#也规定,不要求任何特殊语法,就可将对象转换成对应的任何基类,由于面相基类的转换默认为是类型安全的。类型假装是不少安全问题的根源,也会破坏程序的健壮性和稳定性。数据结构
is 运算符专门用来检查对象是否能够兼容的转换为另外的一个类型的对象,永远不会抛出错误,只是会返回true或者false.ide
if(o is Employee) { Employee e = (Employee)o; }
上面的代码中,作了两次类型检查,加强了安全性,但无疑对性能形成必定的浪费。普通的强制类型转换过程:CLR首先必须判断变量(o)引用的对象的实际类型,而后CLR 类型便利继承的层次结构,用每个基础类型去核对制定的类型(Employee)。函数
as 运算符就是为了简化上面的经常使用编程模式而设计的,as运算符只是检查一次对象,若是对象位null就返回位null,而后就进行转换,若是转换不了就返回位null,而永远不会抛出错误。以下代码,if中间只是判断一个是否位null,相比速度会快不少性能
Employee e = o as Employee; if(e != null) { }
命名空间只是对相关类型进行逻辑分组。对于编译器而言,命名空间的做用就是为了类型名称架上点分隔符让名称变得更加长,也更加具备惟一性。this
命名空间只是针对编译器,而CLR对命名空间是无概念的,在访问类型的时候,CLR须要知道类型的完整名称以及该类型定义在哪个程序集里面,这样“运行时”才能正确的找到和加载正确的程序级,而且对其进行操做spa
命名空间,和程序级之间无直接关系,一个命名空间的代码能够出如今多个程序级中,一个程序级也能够有多个命名空间。能够参考,System以及Linq相关程序级和命名空间线程
using运算符,只是为了方便开发人员,少写一些类的全称部分,一样只是针对编译器有用,CLR并不认识这个语法。另一个做用就是容许给类型和命名空间建立别名,以下:设计
using WintellectWidget = Wintellect.Widget public sealed class Program { public static void Main() { WintellectWidget = new WintellectWidget(); } }
若是仍是有在不一样程序级中,同名的类型,此种状况能够经过外部程序级来解决(extern alias)
假定有以下两个类型的定义
internal class Employee { public int GetYearsEmployed(){} public virtual string GetProgressReport(){} public static Employee Lookup(string name){} } internal sealed class Manager:Employee { public overide String GetProgressReport(){} }
以下图,程序以及执行过一段时间,如今即将调用M3方法。JIT将M3的IL代码编译成CPU的指令时,会注意到全部的类型,确保以及加载了具体对应的程序集。利用程序集的元数据,CLR提取与之相关的信息,建立数据结构来表示类型自己
如图中,Employee和Manager类型对象都包含两个成员:类型对象指针和同步索引块。而在定义类型的时候,能够在类型内部定义静态数据字段,俄日这些静态数据字段提供支援的字节在类型对象自身中分配,每个类型对象都包含一个方法表,在方法表中国,类型定义的每个方法都有对应的记录项
而后,M3执行代码构造一个Manager对象,形成在托管堆上建立Manager类型的一个实例对象。此对象也有类型对象指针和同步索引块,同时还包含必要的字节来容纳Manager类型定义的全部实例数据字段,以及容纳有Manager的任何基类定义的全部实例字段。
任什么时候候在堆上建立对象时,CLR会自动初始化内部的"类型对象指针”成员来引用和对象对应的类型对象(也就是Manager类型对象),在执行构造函数以前还会先初始化同步索引块,而且将对象的实例字段设置为null或者0.而后经过new操做符返回地址
M3的下一行代码是调用Employee的静态方法 Lookup 获得一个Manager对象。调用静态方法的时候,CLR会首先定位到静态方法的类型对应的类型对象,而后,JIT编译器在类型对象方法表中查找与被调用方法的对应的记录项,对方法进行JIT编译,而后在调用编译好的代码。
M3接下来调用非虚实例方法,GetYearsEmployed。调用虚方法时,JIT编译器会找到发出调用的那个变量(e)的类型(Employee)对应的类型对象。若是找不到,就回溯到 object 类,之因此能回溯,是由于每个对象都有一个字段引用了它的基类类型。从方法记录表中找到被调用方法的记录项后,进行JIT编译,编译完成后,在进行方法的调用。
接下来调用的是Empoloyee的虚实例方法GetProgressReport。调用时,JIT要先在方法中生成一些额外的代码,每次都会执行这些代码。这些代码,首先检查发出调用的变量,而后跟随地址来到发出调用的对象,变量e引用的是 manager 对象,而后代码检查对象的内部的“类型对象指针”成员,改为员指向了实际的类型。而后在对类型的对象方法表中查找被调用的方法的记录向,对方法进行JIT编译,而且执行。
Employee和Manager类型对象都会包含一个“类型对象指针”成员,因为类型对象自己也是对象,CLR在建立类型对象时,必须初始化这些成员。CLR开始在进程中运行时,会当即为MSCorLib.dll中定义的System.Type类型对象建立一个特殊的类型对象。Employee和Manager类型对象都是改类型的“实例”,所以,他们的类型对象指针成员都会初始化成对System.Type类型对象的引用。固然,System.Type类型对象自己也是对象,内部也有“类型对象指针”成员,这个指针就指向了他自己,由于这个System.Type类型对象自己是一个类型对象的“实例”。而System.Object的GetType方法返回存储在指定对象的“类型对象指针”成员中的地址,也就是说,GetType方法返回执行对象的类型对象指针,这样就能够判断任何对象的真实类型。