8.7 分部方法
重写基类的虚方法
使用继承基类让子类重写基类虚方法的做法存在很多问题,如下所示代码。
//工具生成的代码,存储在某个代码文件中 internal class Base { private String m_name; protected virtual void OnNameChanging(String value){} public String Name { get{return m_name;} set{ OnNameChanging(value.ToUpper()); m_name=value; } } } //开发人员生成的代码。存储在另一个源代码文件中 internal class Derived:Base { protected override void OnNameChanging(string value) { if(String.IsNullOrEmpty(value)) throw new ArgumentNullException("value"); } }
1.类型必须是非密封的,也不能用于值类型,因为值类型是隐式密封的。此外不能用于静态方法,因为静态方法不能重写。
2.效率问题。定义一个类只为了重写一个方法,会浪费少量系统资源。即使不想重写某个方法,基类代码仍需调用一个什么都不做直接就返回的虚方法。
分布方法的实现
利用C#的分部方法就可以解决以上问题的同时覆盖类的行为。
//工具生成的代码,存储在某个代码文件中 internal sealed partial class Base { private String m_name; partial virtual void OnNameChanging(String value){} public String Name { get{return m_name;} set{ OnNameChanging(value.ToUpper()); m_name=value; } } } //开发人员生成的代码。存储在另一个源代码文件中 internal sealed partial class Base { partial void OnNameChanging(string value) { if(String.IsNullOrEmpty(value)) throw new ArgumentNullException("value"); } }
1.类现在密封,事实上类可以是静态类甚至可以是值类型。
2.详细介绍partial关键字见第六章6.5节。
好处是,可以重新运行工具,在新的源代码文件中生成新的代码,但你自己的代码是存储在一个单独的文件中,不会受到影响。
性能提升
此外,分部方法还提供了另一个巨大的提升。如果不想修改工具生成的类型的行为,那么根本不需要提供自己的源代码文件。
如果只是对工具进行编译,编译器会改变生成的IL代码和元数据,使工具生成的代码变成这样。
//如果没有分布方法的实现 //工具生成的代码在逻辑上等价于以下代码 internal class Base { private String m_name; public String Name { get{return m_name;} set{ OnNameChanging(value.ToUpper()); m_name=value; } } }
也就是说没有实现分部方法,编译器不会生产任何代表分部方法的元数据、IL指令。
规则和原则
1.他们只能在分部类或者结构声明。
2.分部方法返回的始终是void,任何参数都不能用out修饰符来标记。
3.分部方法的声明和实现必须具有完全一致的签名。
4.分部方法总是被视为private方法。但C#编译器禁止在分布方法声明之前天机private关键字