4.1 所有类型都是从System.Object派生
CLR要求所有对象都用new操作符来创建
Employee e=new Employee("ConstructorParam1");
new操作符所做的事情:
1.计算类型以及所有基类中定义的实例字段所需要的字节数
2.在托管堆中分配指定类型要求的字节数,分配对象的内存
3.它初始化对象的“类型对象指针”和“同步块索引”成员
4.调用类型的实例构造器,编译器会在构造器中自动生成代码来调用一个基类构造器,每个类型的构造器负责初始化有这个类型定义的实例字段,最终调用System.Object的构造器
new 执行了所有这些操作之后,会返回指向新建对象的一个引用,这个引用保存在变量e中
4.2类型转换
CLR允许一个对象转换为它的实际类型或者它的基类型。
类型安全性测验:
internal class B{} //基类
internal class D:B{} //派生类
表格:
语句 | 编译成功和执行 | 编译时错误 | 运行时错误 |
Object o1=new Object(); | √ | ||
Object o2=new B(); | √ | ||
Object o3=new D(); | √ | ||
Object o4=o3; | √ | ||
B b1=new B(); | √ | ||
B b2=new D(); | √ | ||
D d1=new D(); | √ | ||
B b3=new Object(); | √ | ||
D d2=new Object(); | √ | ||
B b4=d1; | √ | ||
D d3=b2; | √ | ||
D d4=(D)d1; | √ | ||
D d5=(D)b2; | √ | ||
D d6=(D)b1; | √ | ||
B b5=(B)o1; | √ | ||
B b6=(D)b2; | √ |
使用C#is和as操作符来转型
is:检查一个对象是否兼容于指定的类型,并返回一个Boolean值,is永远不会抛出异常
if(o is Employee){
Employee e=(Employee)o;
//if语句剩余的部分中使用e
}
as:如果兼容指定类型就返回一个指定类型非null的引用,不兼容,返回null
Employee e=o as Employee;
if(e!=null){
//在if语句中使用e
}
一般情况as用的比较多,因为使用is会检查两次对象的类型,首先核实o是否兼容Employee,如果是执行if内语句在核实o是否引用一个Employee。性能没有as高。
4.3命名空间和程序集
C# using 指令
潜在问题:可能有两个或多个类型在不同的命名空间中具有相同的名称
例如:Microsoft下有个Widget的类型,Wintellect下也有个Widget的类型
using Microsoft;
using Wintellect;
Widget w=new Widget(); //一个不明确的引用
解决方案:
//将WintellectWidget定义成Wintellect.Widget的别名
using WintellectWidget=Wintellect.Widget;
注意:命名空间和程序集(实现了一个类型的文件)不一定是相关的。特别是,同一个命名空间中的各个类型可能是在不同的程序集中实现的。
例如System.IO.FileStream类型是在MSCorLib.dll程序集中实现的,而System.IO.FileSystemWatcher是在System.dll中实现的。
4.4运行时相互联系
1.类型、对象、线程栈和托管堆在运行时的相互关系
线程栈:1个线程创建时,会分配一个1MB大小的栈,这个栈的空间用于向方法传递实参,并用于方法内部定义的局部变量。
由于线程栈是从高位开始分配内存,先分配的我就画在上面了,在调用M1()方法时,分配内存的顺序是:name->s->M2的返回地址->length->tally;
回收内存的顺序当然是反过来的。在一个方法中,应该包含一些序幕代码,进行一些初始化工作,还有一些尾声代码,等方法执行完成之后做一些回收工作。
由于方法的返回地址先分配,在方法执行完成的时候回到返回地址,递归太深就容易出现栈溢出,因为参数、局部变量都必须等到方法返回的时候才能回收。
2.调用静态方法、实例方法和虚方法的却别
假设有2个类定义:
internal class Person { private int height; //实例方法 public void SetHeight(int height) { this.height = height; } //虚方法 public virtual void Say(string word) { } //静态方法 public static string Head() { return "my head"; } public static int Age = 10; } internal class Student : Person { public override void Say(string word) { Console.WriteLine(word); ; } } 现在马上就要调用Main入口方法: static void Main(string[] args) { Person student = new Student(); student.Say("Hello cth"); student.SetHeight(172); Person.Head(); Console.ReadLine(); }
1.JIT将Main的IL代码转换成本地CPU指令,然后将内部引用的所有类型的程序集加载,然后创建数据结构来表示类型本身,如图所示
2.当CLR确定方法需要的所有类型对象都以创建,开始允许线程执行Main的本地代码,然后Main执行它的代码来构造一个Student对象,这造成托管堆中创建了Student的一个实例
略。。。。以后单独拿出一篇来介绍