类型和成员基础
类型的各种成员
类型中可定义0个或多个以下种类的成员:
- 常量,指出数据值恒定不变的符号。总是与类型关联,不与类型的实例关联,总是静态成员。
- 字段,表示只读或可读/可写的数据值。字段可以是静态的(类型状态的一部分),也可以是实例(对象状态的一部分)。建议将字段声明为私有,防止被类型外部的代码破坏。
- 实例构造器,将新对象的实例字段初始化为良好初始状态的特殊方法。
- 类型构造器,将新类型的静态字段初始化为良好初始状态的特殊方法。
- 方法,更改或查询类型或对象状态的函数。作用于类型称为静态方法,作用于对象称为实例方法。
- 操作符重载,定义了当操作符作用于对象时,应如何操作该对象,实际是方法。不是CLS的一部分。
- 转换操作符,定义如何隐式或显式将对象从一个类型转型为另一种类型的方法。不是CLS的一部分。
- 属性,允许用简单的、字段风格的语法设置或查询类型或对象的逻辑状态,同时保证状态不被破坏。作用于类型称为静态属性,作用于对象成为实例属性。可以无参也可以多参(集合类中多见)。
- 事件,静态事件允许类型向一个或多个静态或实例方法发送通知。实例事件允许对象向一个或多个静态或实例方法发送通知。事件包含两个方法,允许静态或实例方法登记或注销对该事件的关注。除此之外,事件通常还用一个委托字段来维护已登记的方法集。
- 类型,可定义其他嵌套类型,以将复杂的类型分解为更小的构建单元。
类型的可见性
public不仅对定义程序集中的所有代码可见,还对其他程序集中的代码可见。
internal类型仅对定义程序及中的代码可见。
不显式指定时,C#编译器默认指定为internal。
友元程序集
生成程序集时可用System.Runtime.CompilerServices命名空间中的InternalsVisibleTo特性标明它认为是友元的其他程序集。
[internal sealed class SomeInternalType { ... }
]
C#编译器在编译友元程序集(不含InternalsVisibleTo特性的程序集)时要求使用编译器开关/out:
如果使用C#编译器的/t:module开关编译模块,而该模块将称为某个友元程序集的一部分,则需要使用C#编译器的/moduleassemblyname:
成员的可访问性
定义类型的成员时,可指定成员的可访问性。
CLR术语(C#术语)
Private(private)
Family(protected)
Family and Assembly(不支持)
Assembly(internal)
Family or Assembly(protected internal)
Public(public)
派生类型重写基类型定义的成员时,C#要求原始成员与重写成员具有相同的可访问性(C#限制,非CLR限制)。CLR允许放宽但不允许收紧成员的可访问性限制。
静态类
永远不需要实例化。
只有static成员,组合一组相关成员。
- 静态类必须直接从基类System.Object派生。
- 静态类不能实现任何接口。
- 静态类只能定义静态成员(字段、方法、属性和事件)。
- 静态类不能作为字段、方法参数或局部变量使用。
分部类、结构和接口
partial关键字告诉C#编译器,类、结构或接口的定义源代码可能分散到一个或多个源代码文件中,其优势在于:
- 源代码控制
- 在同一个文件中将类或结构分解成不能的逻辑单元
- 代码拆分
组件、多态和版本控制
组件软件编程(Component Software Programming,CSP)具有以下特点:
- 组件(.NET Framework称为程序集)有“已经发布”的意思
- 组件有自己的标识(名称、版本、语言文化和公钥)
- 组件永远维持自己的标识
- 组件清楚指明它所依赖的组件(引用元数据表)
- 组件应编档它的类和成员。C#通过源代码内的XML文档和编译器的/doc命令行开关提供该功能
- 组件必须指定它需要的安全权限。CLR的代码访问安全性机制提供该功能
- 组件要发布在任何“维护版本”中都不会改变的接口(对象模型)。
C#提供了5个能影响组件版本控制的关键字,可应用于类型或类型成员。
- abstract,表示为了构造派生类型的实例,派生类型必须重写并实现这个成员
- virtual,表示这个成员可由派生类型重写
- override,表示派生类型正重写基类型成员
- sealed,表示改类型不能用作基类型;表示这个成员不能被派生类型重写,只能将该关键字应用于重写虚方法的方法
- new,应用于嵌套类型、方法、属性、事件、常量或字段时,表示该成员与基类中相似的成员无任何关系
CLR如何调用虚方法、属性和事件
在类型上执行操作称为静态方法,在类型的实例上执行操作称为非惊天方法。
CLR提供两个方法调用指令:
- call,该IL指令可调用静态方法、实例方法和虚方法。常用于以非虚方式调用虚方法。
- callvirt,该IL指令可调用实例方法和虚方法,不能调用静态方法。由于要生成代码来验证变量值是否为null,callvirt执行速度比call稍慢。
合理使用类型的可见性和成员的可见性
密封类的好处:
- 版本控制
- 性能
- 安全性和可预测性
一些建议: - 定义类时,除非确定杠幺将其作为基类,并允许派生类对它进行特化,否则总是显式指定为sealed类。
- 类的内部,将数据字段定义为private。
- 类的内部,总是将自己的方法、属性和事件定义为private和非虚。
- 当算法的实现开始变得复杂时,定义一些辅助类型来封装独立的功能。
对类型进行版本控制时虚方法的处理
略(virtual,override,new关键字)