6.1类型的各种成员
在一个类型中可以定义0个或多个以下种类的成员:
- 常量:是指出数据值恒定不变的一个符号。这些符号通常用于使代码更容易阅读和维护。常量通常与类型关联,不与类型的实例关联。从逻辑上讲,常量始终是静态成员。常量就是一个符号,编译时编译器就会将该符号替换成实际值。
public sealed class SomeType { private const Int32 SomeConstant = 1; }
- 字段:字段表示一个只读或可读可写的数据值。字段可以是静态的,这种情况下,字段被认为是类型状态的一部分。字段也可以是实例(非静态),这种情况下,字段被认为是对象状态的一部分。强烈建议将字段声明为私有的,防止类型或对象的状态被该类型外部的代码破坏。详见第7章。
- 实例构造器:将对象的实例字段初始化为良好初始状态的一种特殊方法。
public sealed class SomeType { public SomeType(Int32 x) { } public SomeType() { } }
- 类型构造器:将类型的静态字段初始化为良好初始状态的一种特殊方法。
public sealed class SomeType { static SomeType() { } }
- 方法:方法是一种特殊的函数,作用是更改或查询一个类型或对象的状态。作用于类型时,称为静态方法;作用于对象时,称为实例方法。方法一般会对类型或对象的字段执行读写操作。详见第8章。
- 操作符重载: 操作符重载实际是一种方法,它定义了将一个特定的操作符作用于对象时,应该如何操作这个对象。
- 转换符重载:转换操作符是定义如何隐式或显示的将对象从一种类型转型为另一种类型的方法。
- 属性:领用属性(property),可以使用一种简单的、字段风格的语法来设置或查询类型或对象的部分逻辑状态,同时保证状态不被遭到破坏。作用于类型的称为静态属性,作用于对象的称为实例属性。属性可以是没有参数的,也可以是有多个参数的。详见第10章。
- 事件:利用静态事件,一个类型可以向一个或多个静态或实例方法发送通知。而利用实例事件,一个对象可以向一个或多个静态或实例方法发送通知。提供事件的类型或对象的状态发生改变,通常就会引发事件。事件包含两种方法,允许静态或实例方法登记或注销对该事件的关注。详见第11章。
public sealed class SomeType { public event EventHandler SomeEvent; }
- 类型:类型可定义嵌套于其中的其他类型。通常用这个办法将一个大的、复杂的类型分解成更小的构建单元(building block),以简化实现。
public sealed class SomeType { private class SomeNestedType { } }
无论使用什么编程语言,它的编译器都必须能处理你的源代码,为上述列表中的每一种成员生成元数据和IL代码。
无论使用的编程语言是什么,元数据的格式都是完全一致的。正是因为这个特点,才使CLR成为公共语言运行时。
CLR使用公共元数据格式决定常量、字段、构造器、方法、属性和事件在运行时的行为。元数据是整个.Net Framework开发平台的关键,它实现了编程语言、类型和对象的无缝集成。
6.2类型的可见性
在文件范围内定义类型时,可以将类型的可见性指定为public和internal。
public类型不仅对它的定义程序集中的所有代码可见,还对其他程序集中的代码可见。
internal类型仅对定义程序集中的所有代码可见,对其他程序集中的代码不可见。
定义类型时,如果不显式指定类型的可见性,C#编译器默认将类型的可见性设为internal(两者中较有限的那一个)。
6.3成员的可访问性
在代码中引用一个成员时,成员的可访问性(accessibility)指出这种引用是否合法。
- private:成员只能由 包含该成员的类型或任何嵌套类型中的方法访问。
- protected:访问仅限于当前类型和其派生类型。
- internal:访问仅限于所属程序集中的方法访问。
- internal (or) protected:访问仅限于当前程序集或其派生类型(派生类型可以不属于当前程序集)。
- public:访问不受限制,成员可由任何程序集的任何方法访问。
当然,任何成员想要被别人访问到,都必须在一个可见的类型内定义。
例如,如果程序集AssemblyA定义了一个internal类型,该类型有一个public方法,那么程序集AssemblyB中的代码不能调用AssemblyA中的public方法,因为internal类型对于AssemblyB是不可见的。
在C#中,如果没有显式声明成员的可访问性,编译器通常默认选择private(限制最大的那个)。CLR要求接口类型的所有成员都具有public可访问性。
6.4静态类
静态类是不能实例化的,例如Console,Math,Environment和ThreadPool类。
静态类只有static静态成员。我们直接使用它的属性与方法,静态类最大的特点就是共享,作用是将一组相关的成员组合到一起。
例如Math类中定义了一组执行数学运算的方法。Math 类:为三角函数、对数函数和其他通用数学函数提供常数和静态方法。
static关键字只能应用于类,不能应用于结构(值类型)。这是因为CLR总是允许值类型实例化。
静态类的主要特点如下:
- 它们仅包含静态成员。
- 它们不能被实例化。
- 它们是密封的。
- 它们不能包含实例构造函数。
C#编译器对静态类进行了如下限制:
- 静态类必须直接从基类System.Object派生,从其他任何基类派生没有任何意义。继承只适用于对象,而你不能创建静态类的实例
- 静态类不能实现任何接口,这是因为只有使用类的一个实例时,才可以调用类的接口方法
- 静态类只能定义静态成员(字段、方法、属性和事件),任何实例成员都将导致编译器报错
- 静态类不能作为字段、方法参数或局部变量使用,因为它们都代表引用了一个实例的变量,这是不允许的
- 静态类在编译后,会生成一个被标记为abstract和sealed的类,同时编译器不会生成实例构造器(.ctor方法)
在类或结构内部定义的类型称为嵌套类型。例如:
class Container { class Nested { Nested() { } } }
通过static关键字修饰的成员,是属于类。而实例成员是属于对象。在这个类第一次加载的时候,这个类下面的所有静态成员会被加载。
实例构造函数:使用 new 表达式创建某个类的对象时,会使用实例构造函数创建和初始化所有实例成员变量。
class CoOrds { public int x, y; // constructor public CoOrds() { x = 0; y = 0; } public CoOrds(int x, int y) { this.x = x; this.y = y; } } class MainClass { static void Main() { CoOrds p1 = new CoOrds(); CoOrds p2 = new CoOrds(5, 3); } }
6.5 局部类型
partial这个关键字告诉C#编译器,一个类、结构或者接口的定义源代码可能要分散到一个或多个源代码文件中。
局部类型适用于以下情况:
- 类型特别大,不宜放在一个文件中实现。
- 一个类型中的一部分代码为自动化工具生成的代码,不宜与我们自己编写的代码混合在一起。
- 需要多人合作编写一个类。
局部类型的注意点:
- 关键字partial是一个上下文关键字,只有和 class、struct、interface 放在一起时才有关键字的含义。因此partial的引入不会影响现有代码中名称为partial的变量。
- 局部类型的各个部分一般是分开放在几个不同的.cs文件中,但C#编译器允许我们将他们放在同一文件中。