1.Class的可见性有public和internal两种,public对所有程序集都可见,internal仅对其所在的程序集可见。默认是public的。
2.友元程序集,
使用friend assembly可以实现单元测试,而不使用反射技术。
书上讲的是按照命令行编译。
我测试用的是vs2005的solution,如下:
3.成员的可访问性
成员默认是private的,接口类型的成员都是public的。
子类重写父类的成员时,原始成员与重写成员要有相同的可访问性——C#的约束;CLR的约束是,重写成员的可访问性不能更低。
CLR和C#是不一样的,如表:
CLR术语 | C#术语 |
Private | private |
Family | protected |
Family and Assembly | 不支持 |
Assembly | internal |
Family or Assembly | protected internal |
Public | public |
4.静态类
static只能用于class,不能用于struct,因为CLR要求值必须实例化,而且不能控制实例化过程。
C#对静态类的约束:
静态类必须直接从System.Object派生
静态类不能实现任何接口
静态类只能定义静态成员:字段,方法,属性,事件
静态类不能用作:字段,方法,参数,局部变量。
在MSIL中,不会为静态类生成ctor,会将其标记为abstract和sealed
5.部分类
CLR不支持partial,只是C#的语法。所以某个类型的源码必须使用同一种编程语言
6.组件,多态和版本控制
.NET版本号2.7.1.34,包含4个部分:主版本号,次版本号,内部版本号,修订版本号。
修订版本,向后兼容,改变内部/修订版本号;
发布新版本,不向后兼容,改变主/次版本号。
多态中,子类重写父类的虚方法,会引起版本控制问题,即父类发生改变,其版本低于子类版本,会导致子类行为变化。
C# 5个用于 类/类成员 的 影响组件版本控制 的 关键字:
abstract:用于类/类成员
virtual和override:用于成员
sealed:用于类/类成员。用于成员时,仅用于重写了虚方法的方法。
new,用于类/类成员/常量/字段
C#调用虚方法:
CLR允许类中定义多个"同名方法",仅仅是返回类型不同,IL允许这样做;C#不允许,忽略返回值的类型,相应的用"转换操作符"实现IL中的"同名方法"。
调用方法相应的MSIL:
一个是call,用来调用静态方法,实例方法和虚方法。必须要指定调用方法的类型(对于静态方法)或者对象(对于实例方法/虚方法),如果在该类型/对象中找不到该方法,会检查其基类来匹配方法。
另一个是callvirt,用来调用实例方法和虚方法,不能用于调用静态方法。必须要指定调用方法的实例对象,如果这个对象为null,会抛出NullReferenceException异常,这意味着每次调用前都会有额外的null检查,从而比调用call慢一些。
如下代码所示:
public sealed class Program
{
public Int32 GetFive()
{
return 5;
}
public static void Main()
{
Program p = null;
Int32 x = p.GetFive(); //在C#中,使用callvirt,会抛出NullReferenceException异常
}
}
在C#编译器中,使用callvirt调用所有实例方法(包括虚方0),使用call调用所有静态方法。对于其他的编译器,这一点不能保证,所以在虚方法和非虚方法之间改动而不重新编译,会产生无法预测的问题。
C#使用call而不用callvirt调用虚方法的特例:ToString,见下:
internal class SomeClass
{
public override string ToString()
{
return base.ToString();
}
}
这时候,生成call的IL代码。因为如果使用callvirt,意味着这时一个虚方法,从而递归执行该方法,直到AppDomain的堆栈溢出。
在调用值类型定义的方法时,使用call。这是因为,首先e值类型是密封的,从而不存在虚方法;另外,值类型永远不会为null,所以永远不会抛出NullReferenceException异常;再者,如果使用callvirt,就要使用装箱机制,性能会有极大影响。
在设计class的过程中,要尽量少定义虚方法。取代办法:可以定义一组重载方法,经其中最复杂的方法虚拟化 ,而将所有有用的重载非虚拟化,示例如下:
public class Set
{
private Int32 m_length = 0;
//这个有用的重载是非虚拟的
public Int32 Find(Object value)
{
return Find(value, 0, m_length);
}
//这个有用的重载是非虚拟的
public Int32 Find(Object value, Int32 startIndex)
{
return Find(value, 0, m_length - startIndex);
}
//功能最丰富的方法是虚拟的,可以被重写
public Int32 Find(Object value, Int32 startIndex, Int32 endIndex)
{
.//具体实现
}
}
sealed密闭类尽量使用。将sealed改为非密闭的容易,反之困难;性能也快,因为sealed一定是非虚拟的,从而编译器不用考虑其派生类,而虚方法的性能不如非虚方法;因为密闭了,所以安全性和可预测性也就高。
子类中有父类的方法,会警告,不会报错。这时要使用new关键字,告诉CLR,新旧方法没有任何关系。
补充:
1.静态成员是在堆上分配的。CLR在创建TYPE对象的时候,静态数据成员也会创建
2.实例类中可以有静态成员
系列文章:
CLR笔记:1.CLR的执行模型
CLR笔记:2.生成,打包,部署,管理
CLR笔记:3.共享程序集合强命名程序集
CLR笔记:4.类型基础
CLR笔记:5.基元,引用和值类型
CLR笔记:6.类型和成员基础
CLR笔记:7.常量和字段
CLR笔记:8.方法
CLR笔记:9.Property
CLR笔记:10.事件
CLR笔记:11.字符串
CLR笔记:12.枚举类型和位标志
CLR笔记:13.数组
CLR笔记:14.接口
CLR笔记:15.委托
CLR笔记:16.泛型
CLR笔记:17.自定义属性
CLR笔记:18.可空值类型
版权与免责声明
1、本站所发布的文章仅供技术交流参考,本站不主张将其做为决策的依据,浏览者可自愿选择采信与否,本站不对因采信这些信息所产生的任何问题负责。
2、本站部分文章来源于网络,其版权为原权利人所有。由于来源之故,有的文章未能获得作者姓名,署“未知”或“佚名”。对于这些文章,有知悉作者姓名的请告知本站,以便及时署名。如果作者要求删除,我们将予以删除。除此之外本站不再承担其它责任。
3、本站部分文章来源于本站原创,本站拥有所有权利。
4、如对本站发布的信息有异议,请联系我们,经本站确认后,将在三个工作日内做出修改或删除处理。
请参阅权责声明!