.NET框架为程序员提供了“序列化和反序列化”这一有力的工具,使用它,咱们能很容易的将内存中的对象图转化为字节流,并在须要的时候再将其恢复。这一技术的典型应用场景包括[1] :html
然而,.NET框架提供的默认序列化行为也存在着有诸多限制,尤为是在版本控制方面——好比一个使用SerializableAttribute标记,而未实现ISerializable的类型,在经过重构修改了某个字段的名称后,再反序列化以前的序列化结果时就会失败。程序员
本文首先举例说明了.NET默认序列化方案的限制;而后描述了经过扩展.NET序列化框架而指望达到的目标——咱们已经在实际开发中实现;接下来介绍了.NET序列化框架所提供的扩展点,最后详细说明了如何经过这些扩展点实现一个更易用的序列化方案。数据库
须要特别说明的两点是:网络
在.NET中,为了使某个类型成为可序列化的,最简单的方式是使用SerializableAttribute属性,以下代码所示。app
[Serializable] class Person { private string name; private int agee; ... } var formatter = new BinaryFormatter(); formatter.Serialize(someStream, aPerson);
但在实际的开发项目中,若是真的这样作了,那么在产品的第一个版本发布后,就颇有可能会面临下面的问题。框架
全部这些都不能实现,由于它们所带来的代码修改都将形成软件再也不兼容以前的版本。是的,咱们总能够选择在最开始的时候就使用ISerializable接口(和一个反序列化构造函数),但若是你也有”懒惰“的美德,就会以为这样作很不爽!ide
此外,咱们在开发过程当中还遇到了以下几种状况:函数
而要解决这些问题,都要诉诸于对.NET序列化框架更深刻的理解。工具
在最终的方案中咱们将给出一个PersistedAttribute和一个IPersistable接口,它们与SerializableAttribute和ISerializable很相似,但能解决前面提到的问题。下面的代码说明了PersistedAttribute的使用方法及其功能。优化
// 使用PersistedAttribute代替SerializableAttribute,以使用自定义的序 // 列化机制进行处理。类的名字能够修改,只要保证Persisted的参数不变就 // 能够。 [Persisted("a unique identifier of the class")] class Person { // 与SerializableAttribute不一样,只有明确标记为Persisted的字段 // 或属性才会被序列化。并且每一个序列化项均可以指定一个名称,这 // 样,当字段名称改变后,只要此项的名称不变,就能兼容以前的版 // 本。好比,能够把name改成fullName,而无须作其它任何修改。 [Persisted("Name")] private string name; [Persisted("Age")] private int age; // 对于新添加的字段,经过将其声明为“可选的”,反序列化过程就 // 不会出错,以后能够在IDeserializationCallback中为其赋值,或 // 者能够经过Value属性为其指定默认值。 [Persisted("Id", Optional = true, Value = "any thing")] private string id; // 对于新添加的可选项,也能够为其指定一个计算函数,这样系统在 // 反序列化时若是发现流中没有存储相应的值,就会调用此函数来为 // 其计算相应的值。 [Persisted("Gender", Optional = true, Calculator = CalculateGenderById)] private Gender gender; // 属性也能够被序列化。在序列化时,系统将保存get方法的结果,而 // 反序列化时,则会经过set方法设置属性的值,此方法中的全部逻辑 // 都将会执行。 [Persisted("SomeProperty")] public int SomeProperty { get { ... } set { ... } } }
IPersistable接口则能够与PersistedAttribute进行各类组合、替换,以下面的代码所示。
public interface IPersistable { // 在序列化时获取对象的数据。 void GetObjectData(SerializationInfo info); // 在反序列化时设置对象的数据。 void SetObjectData(SerializationInfo info); } // 与ISerializable同样,实现IPersistable接口也要求有Persisted标记。 [Persisted("a unique identifier of the class")] class Person : IPersistable { // Persisted标记与IPersistable接口能够共存,系统会先处理 // 被标记的字段或属性,而后调用IPersistable接口的成员。 [Persisted("Name")] private string name; // 能够去掉以前使用的Persisted标记,而后在IPersistable // 接口中以一样的名称进行读取或设置。 // [Persisted("Age")] private int age; private Gender gender; void GetObjectData(SerializationInfo info) { info.SetValue("Age", age); // 保存额外的数据。 info.SetValue("G", gender); } void SetObjectData(SerializationInfo info) { // 读取以前使用标记序列化的内容。 age = info.GetInt32("Age"); try { // 处理版本兼容问题。 gender = (Gender)info.GetValue("G"); } catch (Exception e) { } } }
此外,新的方案还提供了IPersistor接口,经过它能够为任何类型提供自定义的序列化代码,无论这个类型是否是可序列化的。
// 能够为其它类型的对象提供序列化功能的接口。 public interface IPersistor { void GetObjectData(object obj, SerializationInfo info); void SetObjectData(object obj, SerializationInfo info); } // 为List<int>类型的对象提供自定义的序列化代码,虽然List<int>自己已是可序列化的. public class IntListPersistor : IPersistor { void GetObjectData(object obj, SerializationInfo info) { var list = (List<int>)obj; // some more efficient codes. ... ... info.SetValue(...); } void SetObjectData(object obj, SerializationInfo info) { var list = (List<int>)obj; // some more efficient codes. ... ... someValue = info.GetValue(...); } } // 能够为其它非可序列化类型提供自定义的序列化代码。 public class RelativePersistor : IPersistor { void GetObjectData(object obj, SerializationInfo info) { var target = (Some3rdPartyNonSerializableClass)obj; ... ... } void SetObjectData(object obj, SerializationInfo info) { var target = (Some3rdPartyNonSerializableClass)obj; ... ... } } // 须要在程序开始的时候将实现类注册到系统中。 PersistManager.Register(typeof(List<int>), new IntListPersistor()); PersistManager.Register(typeof(Some3rdPartyNonSerializableClass, new RelativePersistor());
下面,咱们将介绍相应技术的实现思路。
首先要解决的问题是:如何将自定义的序列化机制插入到.NET序列化框架中。我假设你已经知道如何使用BinaryFormatter或者SoapFormatter,而在此我想简单的描述一下formatter的一些行为细节。后文中咱们将以BinaryFormatter为例。
BinaryFormatter上有一个SurrogateSelector属性(surrogate是“代理”的意思,surrogate selector即是代理选择器),它的类型以下代码所示:
public interface ISurrogateSelector { void ChainSelector(ISurrogateSelector selector); ISurrogateSelector GetNextSelector(); ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector); }
其中用到的ISerializationSurrogate(serialization surrogate即是序列化代理喽)的定义以下:
public interface ISerializationSurrogate { void GetObjectData(Object obj, SerializationInfo info, StreamingContext context); Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector); }
当BinaryFormatter在序列化一个对象obj时,它会检查本身的SurrogateSelector属性是否非空,若是非空,便会以obj的类型为参数调用其GetSurrogate方法,若是此方法返回一个有效的对象surrogate(ISerializationSurrogate),则formatter会调用surrogate.GetObjectData(obj, ...),这时surrogate对象便得到机会来执行自定义的逻辑了。
对,这就是奇迹发生的地方!
咱们要作的就是实现自定义的ISurrogateSelector和ISerializationSurrogate类,在合适的时候调用自定义的代码。而后,在使用时将其注入到BinaryFormatter中,以下面代码所示。
var formatter = new BinaryFormatter( new TheSurrogateSelector, new StreamingContext(StreamingContextStates.All)); formatter.Serialize(stream, objectGraph);
先来看ISurrogateSelector的实现(为简洁起见,去掉了不少优化相关的代码)。
public class PersistSurrogateSelector : SurrogateSelector { private readonly PersistSurrogate inner = new PersistSurrogate(); private readonly ISerializationSurrogate surrogate; public PersistSurrogateSelector() { surrogate = FormatterServices.GetSurrogateForCyclicalReference(inner); } public override ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector) { return inner.CanHandle(type) ? surrogate : base.GetSurrogate(type, context, out selector); } }
其中的PersistSurrogate便是咱们自定义的序列化代理,其代码以下所示:
public class PersistSurrogate : ISerializationSurrogate { public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) { // 使用注册的IPersistor实现来对对象进行序列化和反序列化。 var manager = PersistorManager.Instance; var persistor = manager.GetPersistor(obj.GetType()); persistor.GetObjectData(obj, info); } public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { var manager = PersistorManager.Instance; var persistor = manager.GetPersistor(obj.GetType()); return persistor.SetObjectData(obj, info); } internal bool CanHandle(Type type) { // 若是已经为类型注册了IPersistor接口实现,则自定义机制能够处理。 return PersistorManager.Instance.IsPersistable(type); } }
代码中的PersistorManager就是管理自定义的IPersistor接口实现与相应的类型对应的管理类(前面的代码中提到过),一下子咱们会提到PersistorManager.Instance.IsPersistable的实现,至于此类的其它功能则再也不赘述。
这样咱们就实现了对任意类型进行自定义序列化的功能,下面简要总结一下:
有了这个基础,再实现IPersistable和PersistedAttribute功能就比较简单了,来看PersistorManager::IsPersistable方法的实现:
public bool IsPersistable(Type type) { Utility.CheckNotNull(type); var at = typeof(PersistedAttribute); return Attribute.GetCustomAttribute(type, at, false) != null || exactMatches.ContainsKey(type) || derivedMatches.GetValue(type) != null || dynamicMatches.Any(i => i.CanHandle(type)); }
其中的Attribute.GetCustormAttribute便是在判断具体的类型上是否有PersistedAttribute标记,若是有咱们就能够为其合成一个IPersistor接口的实现——这样,使用PersistedAttribute标记的类型,则再也不须要显示的注册。代码中的exactMatches,derivedMatches和dynamicMatches分别用于处理那些可以为某个具体的类型提供序列化功能,可以为一个派生体系提供序列化功能及可以动态决定是否能够提供序列化功能的IPersistor实现。
咱们能够定义一个Persistor实现来统一处理那些有PersistedAttribute标记的类型,在此以前先来看PersistorManager::GetPersistor的定义:
public IPersistor GetPersistor(Type mapped) { Utility.CheckNotNull(mapped); if (exactMatches.ContainsKey(mapped)) { return exactMatches[mapped]; } var derived = derivedMatches.GetValue(mapped); if (derived != null) { exactMatches[mapped] = derived; return derived; } var dynamic = dynamicMatches.FirstOrDefault(i => i.CanHandle(mapped)); if (dynamic != null) { exactMatches[mapped] = dynamic; return dynamic; } var auto = new Persistor(mapped); exactMatches[mapped] = auto; return auto; }
而Persistor的实现逻辑以下:
至于具体实现代码,咱们这里就省略了。
读到这里,相信你们对.NET的序列化框架的扩展就有所感觉了。不过咱们尚未介绍如何处理类型名称改变的问题,这里只给出一个引子——使用BinaryFormatter的Binder属性和自定义的SerializationBinder派生类——更多的细节相信你们都能搞定的。
其实.NET序列化机制还有不少能够挖掘的地方,好比IObjectReference,每个看似简单的接口都能给咱们无限发挥的空间。
好了,就到这里吧。欢迎你们来探讨。
参考资料: