CLR Via C# 3rd 阅读摘要 -- Chapter 24 – Runtime Serialization
Serialization/Deserialization Quick Start
- 序列化是将一个对象以及相关的对象转换成字节流的过程;反序列化就是序列化的逆过程;
- System.Runtime.Serialization命名空间;
- 当序列化一个对象时,Formatter首先抓取程序集标识,并确定程序集通过调用System.Reflection.Assembly.Load()被装载到执行的AppDomain中;
- 程序集被装载后,Formatter在程序集中查找待反序列化对象所匹配的类型,然后实例化再根据流来初始化实例的字段;
- 一些扩展程序使用Assembly.LoadFrom()来加载程序集,然后从定义在程序集中的类型来构造对象。这些对象在序列化时没有问题,但是反序列化时,Formatter会尝试使用Assembly.Load()来加载程序集,大多数情况下,CLR不能准确定位程序集文件,所以可能会引发SerializationException;
- 因为上面的原因,如果使用Assembly.LoadFrom()来加载程序集,那么强烈建议在反序列化之前处理System.AppDomain.AssemblyResolver事件。
Making a Type Serializable
- 对象在默认情况下是不能序列化的,可以通过给类型加上[Serializable]标签来实现;
- [Serializable]属性不能被子类型所继承;
- System.Object有[Serializable]属性;
- 一般来说,推荐大多数类型为可序列化的?(我的看法是那些可能会被跨边界访问的类型)。
Controlling Serialization and Deserialization
- 至少有两个原因使得你不打算让类型实例的一些字段被序列化:
- 字段的值在反序列化时已经失效了,比如Windows Kernel对象的句柄(文件、进程、线程、互斥体、事件、信号量、……);
- 字段的值可以在反序列化时很简单的重新计算出来。
- 字段加上[NonSerialized]属性标签可以阻止该字段被序列化;
- 如果希望字段在反序列化时被重新计算,可以定义一个方法[OnDeserialized]private void OnDeserialized(StreamingContext context){...};
- 跟[OnDeserialized]相似的几个属性[OnSerializing] -> [OnSerialized] -> [OnDeserializing] -> [OnDeserialized];
- 如果你序列化一个类型的实例,在类型中加入新的字段,然后在尝试反序列化时,Formatter会抛出SerializationException指出成员数目不对,可以通过使用[OptionalField]属性标签来解决该问题。
How Formatters Serialize Type Instances
- FormatterServices.GetSerializableMembers(), .GetObjectData(), GetTypeFromAssembly(), GetUninitializedObject(), .PopulateObjectMembers()静态方法。
Controlling the Serialized/Deserialized Data
- 如何完全的控制序列化/反序列化,可以实现System.Runtime.Serialization.ISerializable接口;
- 一旦类型继承了ISerializable接口,那么子类型也就必须实现该接口;
- 建议在GetObjectData方法和特殊构造器上附加[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]属性标签;
- 当Formatter在序列化对象图时,先查找每一个对象,如果有一个类型实现了ISerializable接口,Formatter就忽略其他所有的客户属性,并构造一个新的System.Runtime.Serialization.SerializationInfo对象;
- 总是调用SerializationInfo.AddValue来加入序列化信息到类型中,如果字段的类型实现了ISerializable接口,别在字段上调用GetObjectData,而是调用AddValue;
- 如果类型是sealed,那么强烈建议将特殊构造声明为private;
- 如果基类没有实现ISerializable接口,怎样为类型实现该接口?FormatterServices.GetSerializableMembers()。
Streaming Contexts
- StreamingContext.State, .Context;
- StreamingContextStates {CrossProcess, CrossMachines, File, Persistence, Remoting, Other, Clone, CrossAppDomain, All};
Serializing a Type as a Different Type and Deserializing an Object as a Different Object
Serialization Surrogates
- System.Runtime.Serialization.ISerializationSurrogate.GetObjectData(), .SetObjectData();
- BinaryFormatter有一个bug阻止从序列化对象到其他的引用。要解决该问题,需要传一个ISerializationSurrogate对象的引用到FormatterServices.GetSurrogateForCyclicalReference()方法;
- SurrogateSelector.AddSurrogate(),多个SurrogateSelector对象可以被链接起来,SurrogateSelector实现了ISurrogateSelector接口。
Overriding the Assembly and/or Type When Deserializing an Object
- System.Runtime.Serialization.SerializationBinder使得反序列化对象到不同的类型非常容易。要做到这点,需要定义的类型继承自抽象类SerializationBinder。
本章小结
本章讲了序列化和反序列化的知识,序列化可以讲对象转换成流进行传输或者持久化,在需要的时候通过反序列化在将对象构造出来。首先讲了如何让类型具备序列化的能力,以及如何控制序列化的过程。然后讲了Formatter如何序列化类型实例,如何控制序列化/反序列化的数据,介绍了StreamingContext对象,演示了如何将对象反序列化到不同的类型实例。接着介绍了序列化代理,最后讲了SerializationBinder抽象类可以用来反序列化对象到不同的类型。