9.16.2 分部方法
分部方法声明由两个部分组成:定义和实现。分部方法包含在分部类或分部结构中。分部类方法的签名和它的可选实现可以位于同一个或两个不同的分部类中。如果未提供分部方法的实现,则编译器将自动移除方法签名,以及对所有其他地方代码对该方法的调用。因此,分部类中的任何方法都可以随意地使用分部方法。和分部类一样,分部方法也会在编译期被合并成一个方法定义。
分部方法有着严格的限制,如下:
❑声明必须以上下文关键字partial开头;
❑声明不能有访问修饰符,因此是隐式私有的;
❑不能有返回值;
❑可以有ref参数,不能有out参数;
❑分部方法可以使用static和unsafe[1]修饰符;
❑参数名称在实现声明和定义声明中虽然可以不同,但仍然推荐使用一致的方法签名。
因为对没有实现的分部方法的调用都会被编译器移除,所以说这些限制是非常有必要的。分部方法的一个典型应用场景就是分离代码生成器生成的代码和用户编写的代码。
接下来,我们来看一个示例代码,如代码清单9-44和代码清单9-45所示。
代码清单9-44 分部代码Car1.cs
1 namespace ProgrammingCSharp4
2{
3 class ClassExample
4{
5 static void Main()
6{
7 Car car=new Car();
8 System.Console.WriteLine(car.DoSomething2());
9}
10}
11
12 partial class Car
13{
14 public string DoSomething1()
15{
16 return"DoSomething1()";
17}
18
19 partial void DoSomething3(string sth1);
20
21}
22}
代码清单9-45 分部代码Car2.cs
1 namespace ProgrammingCSharp4
2{
3 partial class Car
4{
5 public string DoSomething2()
6{
7 DoSomething3("sth");
8 return"DoSomething2()";
9}
10
11 partial void DoSomething3(string sth)
12{
13 System.Console.WriteLine("DoSomething3()");
14}
15}
16}
说明:
我们在Car1.cs中的分部类Car中声明了DoSomething3(代码清单9-44,第19行)方法,这类只是一个方法签名。然后在Car2.cs中为DoSomething3方法提供了实现(代码清单9-45,第11行),注意此处需要包含分部方法的签名,并在DoSomething2方法中调用了DoSomething3方法(代码清单9-45,第7行)。
然后,我们观察一下在提供了DoSomething3方法实现和没有提供该方法实现这两种情况下编译器生成的类Car有哪些区别。
先来看看第一种情况,如图9-16所示。
图 9-16 提供了DoSomething3方法实现时的类Car
可以看到,Car类中包含DoSomething3代码,签名和分部方法的签名一致。这里需要特别关注DoSomething2的代码,如代码清单9-46所示。
代码清单9-46 DoSomething2方法的CIL代码
1.method public hidebysig instance string
2 DoSomething2()cil managed
3{
4//Code size 23(0x17)
5.maxstack 2
6.locals init([0]string CS$1$0000)
7 IL_0000:nop
8 IL_0001:ldarg.0
9 IL_0002:ldstr"sth"
10 IL_0007:call instance void
ProgrammingCSharp4.Car:DoSomething3(string)
11 IL_000c:nop
12 IL_000d:ldstr"DoSomething2()"
13 IL_0012:stloc.0
14 IL_0013:br.s IL_0015
15 IL_0015:ldloc.0
16 IL_0016:ret
17}//end of method Car:DoSomething2
在第10行可见对DoSomething3方法的调用。
接下来,将代码清单9-45中的第11~14行的注释去掉,如代码清单9-47所示。
代码清单9-47 注释掉DoSomething3方法实现的情况
1 namespace ProgrammingCSharp4
2{
3 partial class Car
4{
5 public string DoSomething2()
6{
7 DoSomething3("sth");
8 return"DoSomething2()";
9}
10
11//partial void DoSomething3(string sth)
12//{
13//System.Console.WriteLine("DoSomething3()");
14//}
15}
16}
此时,DoSomething3方法的实现已经去除了,因为DoSomething2方法调用了未实现的DoSomething3方法,因此编译器会将此调用移除,而不会导致编译时错误或运行时错误,编译后的Car如图9-17所示。
图 9-17 未提供DoSomething3方法实现时的类Car
再观察一下DoSomething2方法的CIL代码,观察它和代码清单9-46的异同,如代码清单9-48所示。
代码清单9-48 DoSomething2方法的CIL代码
.method public hidebysig instance string
DoSomething2()cil managed
{
//Code size 11(0xb)
.maxstack 1
.locals init([0]string CS$1$0000)
IL_0000:nop
IL_0001:ldstr"DoSomething2()"
IL_0006:stloc.0
IL_0007:br.s IL_0009
IL_0009:ldloc.0
IL_000a:ret
}//end of method Car:DoSomething2
很显然,代码清单9-48中对DoSomething3方法调用的代码已经被移除。
[1]unsafe关键字表示不安全上下文,该上下文是任何涉及指针的操作所必需的。