30 使用覆写而不是事件处理函数
如果基类已经有一些回调hook了,当然优先选择覆写它们来实现逻辑。其次才是添加事件监听。
31 使用IComparable\<T>和IComparer\<T>实现顺序关系
IComparable\<T>定义了类型的自然顺序,而IComparer\<T>则用来描述其他的顺序。
有一些老的API用的是IComparable而不是泛型版的IComparable\<T>,所以有时候要支持两种。
1 | class Example : IComparable<Example>, IComparable |
(挖坑)
32 避免使用ICloneable接口
(挖坑)
33 仅用new修饰符处理基类更新
派生类可以使用new关键字来修饰某个方法,以达到覆盖基类同名方法的目的。但是这一操作并不能使基类方法变为虚方法,因故也就没有多态的作用。当以基类引用派生类时,所调用的就是基类的方法。
1 | class Base{ |
那么new关键字是为了解决什么问题呢?
假设sdk提供商提供了一个叫Base的类,你写了一个派生类Derived继承与它,顺便加上了你自己的一个方法MyFunc,用着很愉快,就这么平静地使用了几个月。几个月后你们需要对sdk进行更新适配,你突然发现sdk的Base类中同样也出现了一个MyFunc方法!于是你的代码编译报错了,因为派生类中的方法除非加了new关键字,否则不可以和基类方法同签名。
你有这么两种方案可以解决这个问题:
- 怕是Base类的Func是有其特殊用途的,我委屈一下把自己的Func名字改了
- 我觉得Base的Func这个方法根本没卵用,所以我给自己的Func加上new关键字,把Base.Func()覆盖了
34 尽量避免重载基类中定义的方法
这里说的是overload。
1 | class Base{} |
上面的代码,最终的输出是什么呢?
答案是Base in DerivedWriter
。
原因是系统会优先在派生类中找最优重载方法。非常具有迷惑性,所以最好不要这么写。
如果想执行基类的方法,还需转成基类后再调用:
1 | (dWriter as BaseWriter).Func(new Derived()); |