第二十四章 运行时序列化
2013-04-10
24.1 序列化/反序列化快速入门
24.2 使类型可序列化
24.3 控制序列化和反序列化
序列化(serialization)是将一个对象或者对象图转换成字节流的过程。反序列化(deserialization)是将一个字节流转换会对象的过程。在对象和字节流之间转换时非常有用的机制。下面是一些例子:
应用程序的状态(对象图)可以保存到磁盘文件或数据库,并在应用程序下次运行时恢复。如asp.net就是利用它来保持和恢复会话状态的。
一个对象可以轻松复制到系统的剪贴板,在粘贴会同一个或另一个应用程序。windows窗体和wpf就是利用这个功能。
一组对象可以可以轻松的通过网络发给另一台机器上运行的进程。Microsoft .net framework的Remoting架构会对按值封送的对象进行序列化和反序列化。这个技术还可以跨越AppDomain边界发送对象。
除了上述应用,一旦将对象序列化为内存中的一个字节流,可以使用一些更有用的方式来方便的处理数据,比如加密和压缩数据等。
24.1 序列化/反序列化快速入门
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 using System.IO; 2 using System.Reflection; 3 using System.Runtime.Serialization; 4 using System.Runtime.Serialization.Formatters.Binary; 5 6 internal static class QuickStart { 7 public static void Go() { 8 // Create a graph of objects to serialize them to the stream 9 var objectGraph = new List<String> { "Jeff", "Kristin", "Aidan", "Grant" }; 10 Stream stream = SerializeToMemory(objectGraph); 11 12 // Reset everything for this demo 13 stream.Position = 0; 14 objectGraph = null; 15 16 // Deserialize the objects and prove it worked 17 objectGraph = (List<String>)DeserializeFromMemory(stream); 18 foreach (var s in objectGraph) Console.WriteLine(s); 19 } 20 21 private static MemoryStream SerializeToMemory(Object objectGraph) { 22 // Construct a stream that is to hold the serialized objects 23 MemoryStream stream = new MemoryStream(); 24 25 // Construct a serialization formatter that does all the hard work 26 BinaryFormatter formatter = new BinaryFormatter(); 27 28 // Tell the formatter to serialize the objects into the stream 29 formatter.Serialize(stream, objectGraph); 30 31 // Return the stream of serialized objects back to the caller 32 return stream; 33 } 34 35 private static Object DeserializeFromMemory(Stream stream) { 36 // Construct a serialization formatter that does all the hard work 37 BinaryFormatter formatter = new BinaryFormatter(); 38 39 // Tell the formatter to deserialize the objects from the stream 40 return formatter.Deserialize(stream); 41 } 42 }
FCL提供了两个格式化器:Binaryformatter和SoapFormatter。要序列化一个对象图,只需调用格式化器的Serialize方法 。方法原型如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public void Serialize(Stream serializationStream, object graph);
格式化器调用Serialize方法是,为了确保对象图中所有对象都被序列化到流中,格式器会参考每个类型的元数据。序列化时,利用反射来查看每个对象类型中有哪些实例字段,这些实例字段中,又有哪些引用了其他对象,然后对他们进行序列化。
序列化是应注意:
1)使用相同的格式化器进行序列化和反序列化。
2)序列化一个对象时,类型的全名和类型定义的程序集名称会被写入流。在反序列化是,会用这些信息,会用System.Reflection.Assembly.Load方法加载程序集,再在程序集中找到匹配的类型,找到后创建类型的实例,并用流中的值对其字段进行初始化。
24.2 使类型可序列化
FCL得内置类型或者说基元类型已经标识的特性[Serializable],使得他们可序列化。
使类型可序列化语法很简单,只需在类上标上特性[Serializable],它是在System命名空间中定义的。
SerializableAttribute这个特性只能应用于引用类型、值类型。除此之外,这个特性是不会被派生类继承的;反之,则不亦然因此System.Object标识了这个特性。
24.3 控制序列化和反序列化
类标识上特性[Serializable]后,所有实例字段(public,private,protected)都会被序列化,有时我们不希望某些字段被实例化,如下情况:
- 字段之在当前进程内有效,如句柄。
- 字段含有很容易计算的信息
标识字段不需序列化也很简单,只需在字段前标上特性[NonSerializable]即可。注意:该特性不会被派生类继承。
但当一个字段没有序列化,会在反序列化化是出现问题,如某些方法用到这个字段,需要当前值,FCL提供了以下方法:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 [OnSerializing] 2 private void OnSerializing(StreamingContext context) 3 {//在序列化前,修改任何需要修改的状态 } 4 [OnSerialized] 5 private void OnSerialized(StreamingContext context) 6 {//在序列化后,恢复任何需要修改的状态 } 7 8 [OnDeserializing] 9 private void OnDeserializing(StreamingContext context) 10 {//在反序列化前,修改任何需要修改的状态 } 11 [OnDeserialized] 12 private void OnDeserialized(StreamingContext context) 13 {// 在反序列化后,恢复任何需要修改的状态}