规范
第五章 不完全类型
5.1 不完全声明
在定义一个分为多个部分的类型时,要使用一个新的类型修饰符——partial。为了保证和现有代码的兼容性,这个标识符和其他标识符不同:与get和set相同,它不是一个关键字,而且它必须直接出现在关键字class、struct和interface之一的前面。
class-declaration:
attributesopt class-modifiersopt partialopt class identifier type-parameter-listopt
class-baseopt type-parameter-constraints-clausesopt class-body ;opt
struct-declaration:
attributesopt struct-modifiersopt partialopt struct identifier type-parameter-listopt
struct-interfacesopt type-parameter-constraints-clausesopt struct-body ;opt
interface-declaration:
attributesopt interface-modifiersopt partialopt interface identifier type-parameter-listopt
interface-baseopt type-parameter-constraints-clausesopt 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<K,V>
where K: IComparable<K>
where V: IKeyProvider<K>, IPersistable
{
...
}
partial class Dictionary<K,V>
where V: IPersistable, IKeyProvider<K>
where K: IComparable<K>
{
...
}
partial class Dictionary<K,V>
{
...
}
是正确的,因为包含约束的部分(第一个和第二个)分别有效地对同一组类型参数指定了相同的一组类、接口和构造器约束。
5.1.4 基类
当一个不完全类声明中包含指定基类时,允许各个部分包含同样的指定基类。如果一个不完全类型的任何部分都未指定基类,则该类的基类为System.Object。
5.1.5 基接口
分别声明在不同部分中的基接口是指定在各个部分上的基接口的联合。一个特定的基接口在每个部分上只能命名一次,但允许在多个部分上命名同一个接口。基接口中的任何成员只能实现一次。
In the example
在下面的例子中
partial class C: IA, IB {...}
partial class C: IC {...}
partial class C: IA, IB {...}
C类的基接口集合是IA、IB和IC。
通常,一个部分只为该部分上声明的接口提供一个实现,然而,这不是必须的。一个部分可以为另一个不同的部分上声明的接口提供实现:
partial class X
{
int IComparable.CompareTo(object o) {...}
}
partial class X: IComparable
{
...
}
5.1.6 成员
在多个部分中声明的成员只是每个部分中声明的成员的简单聚合。类型声明的所有部分中的类体共享相同的声明空间,并且每个成员的作用域都贯穿所有的部分。任何成员的可访问性总是包含了封闭类型的所有部分;在某一部分中声明的private成员可以在另一部分中自由地访问。如果在多个部分中声明了相同的成员则会产生编译错误,除非这个成员是一个用partial修饰符声明的类型。
partial class A
{
int x; // 错误,不能多次声明x
partial class Inner // 正确,这是一个不完全内部类型
{
int y;
}
}
partial class A
{
int x; // 错误,不能多次声明x
partial class Inner // 正确,这是一个不完全内部类型
{
int z;
}
}
尽管成员的顺序对于C#代码来说并不重要,但对于和其它语言或环境进行接口连接这可能是重要的。在这种情况下,在类型的多个部分中声明的成员的顺序是未定义的。
5.2 名字绑定
尽管可扩展的类型的各个部分必须声明在相同的命名空间中,但各个部分中可以写入不同的命名空间声明。因此,不同的using指令可以出现在各个部分中。当在一个部分中解释简单名字时,仅考虑该部分中声明的命名空间中的using指令。
namespace N
{
using List = System.Collections.ArrayList;
partial class A
{
List x; // x的类型是System.Collections.ArrayList
}
}
namespace N
{
using List = Widgets.LinkedList;
partial class A
{
List y; // y的类型是Widgets.LinkedList
}
}