第五章 不完全类型
5.1 不完全声明
在定义一个分为多个部分的类型时,要使用一个新的类型修饰符—— partial 。为了保证和现有代码的兼容性,这个标识符和其他标识符不同:与 get 和 set 相同,它不是一个关键字,而且它必须直接出现在关键字 class 、 struct 和 interface 之一的前面。
_class-declaration:
attributes opt class-modifiers opt _ partial _ opt _ class _identifier type-parameter-list opt
class-base opt type-parameter-constraints-clauses opt class-body _ ; opt
_struct-declaration:
attributes opt struct-modifiers opt _ partial _ opt _ struct _identifier type-parameter-list opt
struct-interfaces opt type-parameter-constraints-clauses opt struct-body _ ; opt
_interface-declaration:
attributes opt interface-modifiers opt _ partial _ opt _ interface _identifier type-parameter-list opt
interface-base opt type-parameter-constraints-clauses opt interface-body _ ; _ opt _
_ 类声明 _ _:
特性 可选 类修饰符 可选 _ partial _ 可选 _ class _标识符 类型参数列表 可选 _
__ _基类 可选 类型参数约束条款 可选 类体 _ ; _ 可选 _
_ 结构声明 _ _:
特性 可选 结构修饰符 可选 _ partial _ 可选 _ struct _标识符 类型参数列表 可选 _
__ _结构接口 可选 类型参数约束条款 可选 结构体 _ ; _ 可选 _
_ 接口声明 _ _:
特性 可选 接口修饰符 可选 _ partial _ 可选 _ interface _标识符 类型参数列表 可选 _
__ _基接口 可选 类型参数约束条款 可选 接口体 _ ; _ 可选 _
不完全类型声明中的每一部分必须包含 partial 修饰符,并且必须和其他部分位于相同的命名空间中。 partial 修饰符表明在其他位置可能有该类型声明的附加部分,但这些附加部分不是必须的,这就运行一个单独的类型声明包含 partial 修饰符。
不完全类型的所有部分必须放在一起编译,这才能使这些部分在编译期间合并。但是部分类型不允许扩展已经编译过的类型。
嵌套类型可以通过使用 partial 修饰符声明为多个部分。典型的情况是,包含嵌套类型的类型的声明也使用了 partial ,而潜逃类型的各个部分分别声明在这个包含类型的各个不同部分中。
The partial modifier is not permitted on delegate or enum declarations.
partial 修饰符不允许用于委托或枚举声明。
5.1.1 特性( Attribute )
不完全类型的各个部分上的特性将被按照不确定的顺序合并,如果一个特性被放在多个部分上,在相当于在类型上对一个特性使用了多次。例如,下面的两部分:
[Attr1, Attr2("hello")]
partial class A {}
[Attr3, Attr2("goodbye")]
partial class A {}
相当于下面的声明:
[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")]
class A {}
类型参数上的特性按照同样的方式合并。
5.1.2 修饰符
当一个不完全类型声明中包含可访问性说明( public 、 protected 、 internal 和 private 修饰符)时,所有其它部分都可以包含一个同样的修饰符。如果不完全类型的任何一个部分都不包含可访问性说明,这个类型将具有默认的恰当的可访问性。
如果嵌套类型的不完全声明中包含 new 修饰符,在这个嵌套类型隐藏了继承的成员时将不会出现警告。
如果类的不完全声明中的一个或多个部分包含了 abstract 修饰符,则这个类被认为是抽象的。否则,这个类被认为是非抽象的。
如果类的不完全声明中的一个或多个部分包含了 sealed 修饰符,则这个类被认为是密封的。否则,这个类被认为是非密封的。
注意一个类不能既是抽象的又是密封的。
当在不完全类型声明的一个部分上使用了 unsafe 修饰符,则只有这一个特定的部分被认为是在不安全环境中。
5.1.3 类型参数和约束
如果一个分型类型被声明在多个部分中,每个部分都必须声明类型参数。各个部分必须具有相同数量的类型参数,每个类型参数的名称和顺序也必须一样。
若一个不完全泛型类型包含类型参数约束( where 子句),则其它部分中也可以包含同样的约束。不过每个包含约束的部分必须对相同的类型参数的集合进行约束,这个集合中的每个类型参数的类、接口和构造器约束必须相同。如果不完全泛型类型的每个部分均未指定类型参数约束,则这些类型参数被认为是不受约束的。
下面的例子
partial class Dictionary
1<k,v>
2where K: IComparable<k>
3where V: IKeyProvider<k>, IPersistable
4{
5...
6}
7
8partial class Dictionary<k,v>
9where V: IPersistable, IKeyProvider<k>
10where K: IComparable<k>
11{
12...
13}
14
15partial class Dictionary<k,v>
16{
17...
18}
19
20是正确的,因为包含约束的部分(第一个和第二个)分别有效地对同一组类型参数指定了相同的一组类、接口和构造器约束。
21
22### 5.1.4 基类
23
24当一个不完全类声明中包含指定基类时,允许各个部分包含同样的指定基类。如果一个不完全类型的任何部分都未指定基类,则该类的基类为 System.Object 。
25
26### 5.1.5 基接口
27
28分别声明在不同部分中的基接口是指定在各个部分上的基接口的联合。一个特定的基接口在每个部分上只能命名一次,但允许在多个部分上命名同一个接口。基接口中的任何成员只能实现一次。
29
30In the example
31
32在下面的例子中
33
34partial class C: IA, IB {...}
35
36partial class C: IC {...}
37
38partial class C: IA, IB {...}
39
40C 类的基接口集合是 IA 、 IB 和 IC 。
41
42通常,一个部分只为该部分上声明的接口提供一个实现,然而,这不是必须的。一个部分可以为另一个不同的部分上声明的接口提供实现:
43
44partial class X
45{
46int IComparable.CompareTo(object o) {...}
47}
48
49partial class X: IComparable
50{
51...
52}
53
54### 5.1.6 成员
55
56在多个部分中声明的成员只是每个部分中声明的成员的简单聚合。类型声明的所有部分中的类体共享相同的声明空间,并且每个成员的作用域都贯穿所有的部分。任何成员的可访问性总是包含了封闭类型的所有部分;在某一部分中声明的 private 成员可以在另一部分中自由地访问。如果在多个部分中声明了相同的成员则会产生编译错误,除非这个成员是一个用 partial 修饰符声明的类型。
57
58partial class A
59{
60int x; // 错误,不能多次声明x
61
62partial class Inner // 正确,这是一个不完全内部类型
63{
64int y;
65}
66}
67
68partial class A
69{
70int x; // 错误,不能多次声明x
71
72partial class Inner // 正确,这是一个不完全内部类型
73{
74int z;
75}
76}
77
78尽管成员的顺序对于C#代码来说并不重要,但对于和其它语言或环境进行接口连接这可能是重要的。在这种情况下,在类型的多个部分中声明的成员的顺序是未定义的。
79
80## 5.2 名字绑定
81
82尽管可扩展的类型的各个部分必须声明在相同的命名空间中,但各个部分中可以写入不同的命名空间声明。因此,不同的 using 指令可以出现在各个部分中。当在一个部分中解释简单名字时,仅考虑该部分中声明的命名空间中的 using 指令。
83
84namespace N
85{
86using List = System.Collections.ArrayList;
87
88partial class A
89{
90List x; // x 的类型是 System.Collections.ArrayList
91}
92}
93
94namespace N
95{
96using List = Widgets.LinkedList;
97
98partial class A
99{
100List y; // y 的类型是 Widgets.LinkedList
101}
102}</k,v></k></k></k,v></k></k></k,v>