CLR/C# 的一些语言特性
1. new和override区别
override在子类中重写基类的方法;new在子类中为基类同名(virtual)方法添加一个新的版本,会保留基类的同名(virtual)方法。
如果基类的同名方法不给出new和override修饰符,.Net默认采用new的方式,编译时将产生一条警告信息。
关于重载:一般有按名称隐藏和按签名隐藏两种策略,C#默认按签名隐藏,即默认使用hidebysig特性。
2. 构造函数
a) static构造函数.cctor,对类型初始化。在类里面声明的静态变量将由C#编译器自动编译到.cctor中,如果有显示的声明静态构造器,也是编译到.cctor(在.cctor中局部静态变量将位于显示声明的静态构造器前面)。
CLR有两种策略决定.cctor的执行时间,第一种是首次使用类型时,第二种是首次使用类型的静态成员时通过给类型添加beforefieldinit 元数据特性)。C#编译器对没有显示声明静态构造器的类型默认设置beforefieldinit特性,对存在显示静态构造器的类型不会设置。
b) 类的构造函数.ctor。要注意的一点是CLR分配对象时将调用深度派生(most-derived)类型的构造函数方法,在派生类型构造函数的最开始位置调用基类构造方法,不管在派生类型本身还是基类的构造函数中,对象的实际类型都是派生类型。在这样的机制下,有些问题需要谨慎处理,例如如果基类在构造函数中调用被override过的virtual方法,而这时派生类的构造函数并没有执行。
3. abstract, virtual
abstract的类不能实例化,只能用于子类继承。
abstract的方法隐式的就是一个virtual方法;abstract的方法子类必须实现;abstract方法只能有声明,不能包含实现。
virtual方法提供给子类override;可以包含实现。
4. as和强制类型转换
强制类型转换:MyClass myclass=(MyClass)obj; 使用as:MyClass myclass=obj as MyClass;
区别在于,如果obj不能被转换成MyClass类型,强制类型转换会抛出异常,而使用as时不会有异常,只是转换结果(myclass变量)的值为null。
强制类型转换使用castclass操作码(opcode)实现,as转换、is判断使用isinst操作码实现。这两个操作码都使用类型的元数据信息,将对象转换成给定类型放在当前计算堆栈上,只不过无法转换时一个抛异常,另一个将null放在堆栈中。
关于类型转换:每个对象通过header中的类型句柄引用类型元数据,在类型元数据中有一个interface table,指示类型实现了哪些接口(每个项都包含指向接口元数据的类型指针),一个基类类型元数据指针,在接口、继承体系之间的类型兼容性测试主要基于这些信息进行。类型转换另外需要考虑的,是类型是否有重载隐式、显示类型转换操作等。
5. 修饰符
private,只能在同一个类中调用。
protected,可在派生类中调用,无论派生类型是否在同一个assembly中。
internal,只能在同一个assembly中调用。
protected internal,可以在任何assembly的派生类型中调用,也可由同一个assembly中的任何类型调用。
6. readonly / const区别
const在编译之后将常量编译到指令流中,只在元数据保留有常量名和值的信息。readonly的变量只能在声明时提供初始化值,或者在构造函数中初始化,之后变量的值不再允许修改。
7. ref和out区别
都是表示引用方式传递参数,out明确要求调用者进行初始化,ref对调用方和被调用方都没有明确的职责要求。因此对于out型的参数,如果在调用之前没有初始化赋值,会产生一个编译器错误。
override在子类中重写基类的方法;new在子类中为基类同名(virtual)方法添加一个新的版本,会保留基类的同名(virtual)方法。
如果基类的同名方法不给出new和override修饰符,.Net默认采用new的方式,编译时将产生一条警告信息。
关于重载:一般有按名称隐藏和按签名隐藏两种策略,C#默认按签名隐藏,即默认使用hidebysig特性。
2. 构造函数
a) static构造函数.cctor,对类型初始化。在类里面声明的静态变量将由C#编译器自动编译到.cctor中,如果有显示的声明静态构造器,也是编译到.cctor(在.cctor中局部静态变量将位于显示声明的静态构造器前面)。
CLR有两种策略决定.cctor的执行时间,第一种是首次使用类型时,第二种是首次使用类型的静态成员时通过给类型添加beforefieldinit 元数据特性)。C#编译器对没有显示声明静态构造器的类型默认设置beforefieldinit特性,对存在显示静态构造器的类型不会设置。
b) 类的构造函数.ctor。要注意的一点是CLR分配对象时将调用深度派生(most-derived)类型的构造函数方法,在派生类型构造函数的最开始位置调用基类构造方法,不管在派生类型本身还是基类的构造函数中,对象的实际类型都是派生类型。在这样的机制下,有些问题需要谨慎处理,例如如果基类在构造函数中调用被override过的virtual方法,而这时派生类的构造函数并没有执行。
3. abstract, virtual
abstract的类不能实例化,只能用于子类继承。
abstract的方法隐式的就是一个virtual方法;abstract的方法子类必须实现;abstract方法只能有声明,不能包含实现。
virtual方法提供给子类override;可以包含实现。
4. as和强制类型转换
强制类型转换:MyClass myclass=(MyClass)obj; 使用as:MyClass myclass=obj as MyClass;
区别在于,如果obj不能被转换成MyClass类型,强制类型转换会抛出异常,而使用as时不会有异常,只是转换结果(myclass变量)的值为null。
强制类型转换使用castclass操作码(opcode)实现,as转换、is判断使用isinst操作码实现。这两个操作码都使用类型的元数据信息,将对象转换成给定类型放在当前计算堆栈上,只不过无法转换时一个抛异常,另一个将null放在堆栈中。
关于类型转换:每个对象通过header中的类型句柄引用类型元数据,在类型元数据中有一个interface table,指示类型实现了哪些接口(每个项都包含指向接口元数据的类型指针),一个基类类型元数据指针,在接口、继承体系之间的类型兼容性测试主要基于这些信息进行。类型转换另外需要考虑的,是类型是否有重载隐式、显示类型转换操作等。
5. 修饰符
private,只能在同一个类中调用。
protected,可在派生类中调用,无论派生类型是否在同一个assembly中。
internal,只能在同一个assembly中调用。
protected internal,可以在任何assembly的派生类型中调用,也可由同一个assembly中的任何类型调用。
6. readonly / const区别
const在编译之后将常量编译到指令流中,只在元数据保留有常量名和值的信息。readonly的变量只能在声明时提供初始化值,或者在构造函数中初始化,之后变量的值不再允许修改。
7. ref和out区别
都是表示引用方式传递参数,out明确要求调用者进行初始化,ref对调用方和被调用方都没有明确的职责要求。因此对于out型的参数,如果在调用之前没有初始化赋值,会产生一个编译器错误。