可以将类、结构、接口和方法的定义拆分到多个源文件中。每个源文件包含类型或方法的部分定义,在编译时,这些部分会被组合起来。
1.Partial Classes
在以下几种情况需要拆分类定义:
- 对于大型项目,将一个类分部为多个独立文件可以让多个程序员同时对该类进行处理。
- 使用自定生成的源文件时,不用修改文件就可以将代码添加到类中。Visual Studio 在创建 Windows Forms,Web 服务包装代码时都用到了该方法。无需修改Visual Studio 创建的文件你就可以使用这些代码。
- 若要拆分类定义,使用关键字 partial。
如下所示:
public partial class Employee { public void DoWork() { } } public partial class Employee { public void GoToLunch() { } }
partial 关键字说明在命名空间中可以定义类、接口、结构的其他部分。
- 所有部分都要用partial 关键字。
- 编译时,这些部分必须全部可见,从而从来构建最终的类型
- 所有部分必须有相同的访问性,
如果将任意部分声明为抽象,则整个类型被视为抽象类型。如果任意部分声明为 sealed,则整个类型被视为sealed。如果任意部分声明了基类,则整个类型都将继承该类。
指定基类的所有部分必须一致,没有指定基类的部分,则默认继承基类。各个部分可以指定不同的接口,而最终类型将是吸纳所有分部声明的全部接口。在某一部分声明的任何类、结构或接口可供其他部分使用。
NOTE:partial 修饰符不能用于 delegate 和 enumeration声明。
下例演示嵌套类型的partial 定义,即使嵌套它们的类型不是partial也可以。
public partial class Employee { public void DoWork() { } } public partial class Employee { public void GoToLunch() { } }
编译时,partial类型定义的所有 attributes 被合并,例如:
[SerializableAttribute] partial class Moon { } [ObsoleteAttribute] partial class Moon { }
和下面的声明等效于:
[SerializableAttribute] [ObsoleteAttribute] class Moon { }
下面所有项都会被合并:
- XML comments
- interfaces
- generic-type parameter attributes
- class attributes
- members
例如,下面的声明:
partial class Earth : Planet, IRotate { } partial class Earth : IRevolve { }
等效于:
<pre name="code" class="csharp">class Earth : Planet, IRotate, IRevolve { } class Earth : Planet, IRotate, IRevolve { }
1.1 限制
处理分部类定义时,需要遵循以下几个原则:
- 所有的partial-type 定义都要带 partial 修饰符。
- partial 修饰符必须紧靠在 class, struct 或 interface 前面。
- 分部类型定义中允许嵌套的分部类型定义。如:
partial class ClassWithNestedClass {
partial class NestedClass { }
}
partial class ClassWithNestedClass {
partial class NestedClass { }
}
- 所有的分部类型定义必须在同一程序模块(.exe 或 .dll)中。
- 类名和泛型类型参数在所有的分部类型定义中都必须一致。
-
以下是可用于修饰 partial
类型定义的关键字,同一类型不同部分的定义的关键字不能冲突:
- public
- private
- protected
- internal
- abstract
- sealed
- base class
- new modifier
- generic constraints
1.2 例1
下面的示例演示在分部类CoOrds一部分中声明字段和构造函数,另一部分声明成员 PrintCoOrds:
public partial class CoOrds {
private int x;
private int y;
public CoOrds(int x, int y) {
this.x = x;
this.y = y;
}
}
public partial class CoOrds {
public void PrintCoOrds() {
Console.WriteLine("CoOrds: {0},{1}", x, y);
}
}
class TestCoOrds {
static void Main() {
CoOrds myCoOrds = new CoOrds(10, 15);
myCoOrds.PrintCoOrds();
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
// Output: CoOrds: 10,15
1.3 例2
演示分部结构和接口:
partial interface ITest {
void Interface_Test();
}
partial interface ITest {
void Interface_Test2();
}
partial struct S1 {
void Struct_Test() { }
}
partial struct S1 {
void Struct_Test2() { }
}
2.分部方法(Partial Methods)
分部类或结构可以包含分部方法。分部方法包含两部分:声明和定义。声明和定义可以在同一分部,也可以在不同部分。例如:
// Definition in file1.cs
partial void onNameChanged();
// Implementation in file2.cs
partial void onNameChanged()
{
// method body
}
分部方法类似于事件,一部分用于定义方法,另一部分可以决定是否实现该方法。如果未实现该方法,编译器移除方法签名及对该方法的调用,从而对运行时没有任何影响。因此,分部类中可以*使用分部方法,即使没有提供实现。如果该方法没被实现,编译和运行时也不会抛出错误。
分部方法在自定义生成代码时,特别有用。因为方法声明存在,因此允许生成的代码调用该方法,而开发人员可自行决定是否实现该方法。
分部方法定义要点:
- 必须以partial 开头,返回void
- 可以有ref,但不能由 out 参数
- 分部方法为隐式 private,因此不能为virtual
- 分部方法不能为 extern
- 可以有static 和 unsafe 修饰符。
- 可以为泛型。泛型参数放在声明中,也可以在实现中重复声明。类型参数名在声明和实现中不必一样。
- 可以为定义的分部方法定义委托,未定义的分部方法不能定义委托。