C#中.我们可以利用部分类,将一个类分散到多个类文件中,这样我们就可以多个开发者同时开发某个类库,或者是扩展其他开发者发布的类库.甚至是代码生成器生成的代码,例如LINQ2SQL,ADO.NET EF等,以获取更高效的开发.
Re:Class和Class File的区别.这里的类是我们平时所说的普通类-Class,如抽象类,基类,子类等等.而类文件-Class File则是我们平时编写类时所用到的文件,如C#的.cs,VB的.vb.
虽然部分类很好的解决了多个开发者同时开发而互不影响的问题,但其约束十分严格.开发者之间不能/很难彼此沟通,并且无法/很难修改对方的代码.这意味着需要为对方提供很多挂钩.这些挂钩应该以部分方法的方式实现.而另一方的开发者可以选择实现或不实现.
实体框架的出现,大大的减轻了我们的工作量,使我们把更多的重心放在逻辑实现上,而减少对于数据访问的关注.例如我们可以使用LINQ2SQL,LINQ2SQL是一个轻型的实体框架.它生成了我们对数据层的访问.但.有些时候,我们需要扩展些自己的内容,如方法/属性等等.除了直接在生成的类文件中添加修改意外,我们还可以利用部分类扩展我们所需的功能,前者当每次我们跟新实体时.你所扩展的内容都会消失.而部分类依然能很好的为你服务.
为部分类提供最有效的钩子就是部分方法.它和接口的方法声明很相似.
关键字partial+void+方法名+方法参数.
partial void ReportValueChanging(RequestChange args);
由于部分方法可能成为类的一部分(实现),也可能不成为类的一部分(不实现),所以C#本身对于部分方法给出了一些限制.
1.返回类型必须为void.
2.部分方法不能为抽象/虚方法.
3.部分方法不能用来实现接口.
4.参数列表不能包含任何out参数,因为编译器无法初始化这些out参数.
我们利用部分方法来让用户件事或修改类的行为:修改方法,事件处理程序及构造函数.
修改方法是指那些将要修改类中对外可见的状态的方法.我们可以将其理解为任何状态的变化,由于另一个类文件的部分类实现依然是类的一部分.所以我们可以完全控制到该类的所有内部状态.
一般而言,修改方法应该为类提供两个部分方法.第一个在修改前调用,用来让另一方(另一开发者)能够进行合法性验证等等.第二个方法应该在修改状态后调用,用来让另一方相应这个状态的变化.
public partial class GeneratedStuff { private struct ReportChange { public readonly int OldValue; public readonly int NewValue; public ReportChange(int oldValue, int newValue) { this.OldValue = oldValue; this.NewValue = newValue; } } private class RequestChange { public ReportChange Values { get; set; } public bool Cancel { get; set; } } partial void ReportValueChanging(RequestChange args); partial void ReportValueChanged(ReportChange values); private int storage = 0; public void UpdateValue(int newValue) { RequestChange updateArgs = new RequestChange { Values = new ReportChange(storage, newValue) }; ReportValueChanging(updateArgs); if (!updateArgs.Cancel) { storage = newValue; ReportValueChanged(new ReportChange(storage, newValue)); } } }
如果另一个部分类没有提供两个部分方法的实现,那么C#编译器将移除该调用.生成方法如下
public void UpdateValue(int newValue) { RequestChange updateArgs = new RequestChange { Values = new ReportChange(this.storage, newValue) }; if (!updateArgs.Cancel) { this.storage = newValue; } }
并且,两个部分方法也一并移除了.
我们可以实现挂钩如下
public partial class GeneratedStuff { partial void ReportValueChanging(GeneratedStuff.RequestChange args) { if (args.Values.NewValue < 0) { //dosomething... } else { //dosomething... } } partial void ReportValueChanged(GeneratedStuff.ReportChange values) { //dosomething... } }
这里,我们通过一个取消标记来让开发者可以取消某个修改动作,你也可以通过抛出异常得到同样的效果.但.推荐使用布尔型的取消标记,因为这样更加轻量一些
实际上,事件的处理程序和上面的方法修改并无多大的区别.你所需要做的.仅仅只是添加一个委托事件而已.实现了类似控件的事件机制.
无论是生成的代码,还是我们手工编写的代码.我们都无法预计调用类时外部将会调用哪个构造函数.所以.我们只能在内部做一些手脚.例如
public partial class GeneratedStuff { partial void Initialize(); public GeneratedStuff() : this(0) { } public GeneratedStuff(int someValue) { this.storage = someValue; Initialize(); } }
注意!Initialize在最后调用,这样就有了一次检查当前对象状态的机会,可以进行必要的修改,或是对于不符合要求时抛出异常等等.实际上.这种构造函数重载也有效的提升你的代码质量(对于不同的构造函数,结果IL都会生成不同的函数块.而这种重构法最后其实真正生成的函数块只有一个)
经过以上的阐述,相信我们已经更了解部分类是怎样炼成的了.只有更好的了解其背后的一系列"小动作",才有助于我们更好的提升,写出更有质量的代码