21 限制类的可见性
就是说能隐藏的尽量隐藏,后面改起来也会轻松点。
22 通过定义并实现接口方法替代继承
也是一个老生常谈的东西了,面向接口编程。
23 理解接口方法和虚方法的区别
1 | interface IMsg |
实际上B中的Message是一个新的方法,只不过由于同名,把基类的同名方法给覆盖了。
若要让下面那个调用也打印出B,可以用new修饰B.Message()
:
1 | public class B : A |
该方法的缺点当使用A类型引用B对象时,仍会调用A的方法。
欲修改此行为,应直接修改基类的方法为虚方法,再于派生类方法前修饰override关键字。
1 | public class A : IMsg |
如果A类根本不想实现Message方法,把A类变成抽象类即可。
1 | public abstract class A : IDispose |
若是派生类不想此方法再被覆写,可用sealed关键字修饰
1 | public class B : A |
24 用委托实现回调 & 25 用事件模式实现通知
1 | public class EventDispatcher |
26 避免返回堆内部类对象的引用
参见18 区分值类型和引用类型。
27 让类型支持序列化
应该尽可能地为类型添加序列化支持,只要类型表示的不是UI控件、窗体或者表单。理由是如果成员被私有隐藏起来了,那么派生类想要实现序列化时不可能的。
当成员变量都是可序列化的时候(.Net基础类型int, string, etc…),为类型MyClass
添加序列化支持只需要在最前头添加[Serilizable]
即可。但是如果成员变量里有不可序列化的类型,那么在序列化MyClass
的时候就会抛出运行时错误。
在成员变量前添加[NonSerialized]
特性可以避免被序列化。如一些缓存用的成员变量不需要被序列化,便可添加该特性,以节省消耗。
需要注意的是标志了[NonSerialized]
特性的成员变量在反序列化的过程中,.Net Framework的序列化API并不会初始化它们。同时,由于反序列化的时候类的构造函数不会被调用,所以成员的初始化器也就不会被执行。这将导致这些成员将被初始化为初始值0。
为了避免此种弊端,可以实现IDeserializationCallback接口的OnDeserialization()方法来初始化被添加了[NonSerialized]
特性的成员,这个方法会在整个对象被反序列化完成之后调用。然而,在OnDeserialization()执行完成之前,该实例的所有公共成员就可以被外部访问,如果此时OnDeserialization()还未执行完毕,外部访问的就是一个还未初始化完成的成员(多线程下可能会发生此情况)。
蠢逼地絮絮叨叨:如果是使用构造函数new出来的对象,当然就不会有这种情况。毕竟在构造函数完成之前,其他逻辑是无法获取并访问到该对象以及它的任何一个成员的。
(其他待续)
28 提供粗粒度的因特网服务API
其实就是说尽量减少频繁的网络操作。尽量把多个Http请求集合成一个,减少一来一回的时间消耗。
29 支持泛型协变和逆变
??? 我感觉不是很实用?再说吧。