类型和成员基础

访问限制符

c#中,类型默认的修饰符是internal,成员默认的修饰符为private。在c#中的访问限制符和CLR中有对应,就如C#中的基元类型和FCL中有对应一样。下图为CLR VIA C#一书中的表格

image

成员之间的访问,比如类外部不能访问private成员,这些访问限制,在编译时期会进行检测,而且在运行时也会进行访问检查,因为我们可以动态添加程序集,而对程序集中的访问在编译时间不可知,因此运行时也会进行检查。

在C#中,派生类对基类中的虚方法或者其他进行重写的时候,要求其访问限制程度要不低于基类,比如基类有一虚函数protected virtual void test();那么派生类重写的时候,访问修饰符为protected或者public。原因是C#中允许隐式转换(即从派生类转换为基类或者低精度转高精度或者小整数转大整数),如果派生类的方法访问限制大于基类,就不能隐式转换为基类了。

CLR访问虚函数、虚属性、虚事件

属性和事件的本质是函数,CLR是如何访问虚函数、静态函数以及一般的实例函数呢。CLR中有两种访问,call和callvirt两指令。

call :访问静态函数,实例函数,虚函数。假定引用不为null,CLR根据引用的类型在其类型对象的方法表中寻找

callvirt:访问实例函数和虚函数。判断引用是否为null,CLR根据引用指向的类型在其类型对象的方法表中寻找

在C#中,所有的静态函数都由call指令访问,所有的实例函数都由实例函数执行。一般情况下:如果是以虚方式访问虚函数,采用callvirt,采用非虚方式访问虚函数,采用call,比如base.virtfun()。

call和callvirt区别是callvirt会验证引用是否为空,是否指向某一个对象,如果为null,则会抛出NullReferenceException,而call不检查。检查为消耗性能,因此callvirt在性能方面会差一点。因此在类的设计时候如果要使用多态,尽量少使用虚函数,多使用重载。jeff建议:对于简单的函数使用重载,复杂的就使用虚函数。

 

如何判断是以虚方式还是非虚方式访问虚函数呢。比如 Employee e = new Manager(),虚方式;Employee e = new Employee();非虚方式。

 

callvirt访问实例函数时,找到类的类型对象,找到函数的入口,如果没有找到,就向基类对象中寻找,一直向上寻找。callvirt访问虚函数时,先判断对象的真实类型,然后去寻找。

 

image

 

上述都是引用类型,如果是值类型的,那么所有的都是使用call执行,包括构造函数(引用类型是newobj)。

其他

设计类的时候,最好使用sealed关键字,字段声明为private。

C#中为了更好的逻辑性支持partial class,partial structure, parital interface,这都是由编译器支持,CLR并不知情。

静态类里的所有字段、属性、函数以及事件都得static,而且默认是sealed和abstract的,即不能被继承和实例化。

posted @ 2012-09-17 00:16  alab  阅读(287)  评论(0编辑  收藏  举报