赞
踩
序列化是什么?
对象转化为字节流的过程,字节流转化为对象叫反序列化
下面是简单的序列化和反序列化 class MainClass { public static void Main() { var objs = new List<string> { "a", "b", "c" }; Stream stream = SerializeToMemory(objs); objs = null; Console.WriteLine($"{stream.Position}"); var dObj = DeserializeFromMemory(stream); foreach (var item in (List<string>)dObj) { Console.WriteLine(item); } } static MemoryStream SerializeToMemory(Object objectGraph) { MemoryStream memoryStream = new MemoryStream(); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(memoryStream, objectGraph); return memoryStream; } static Object DeserializeFromMemory(Stream stream) { stream.Seek(0, SeekOrigin.Begin); BinaryFormatter bf = new BinaryFormatter(); return bf.Deserialize(stream); } }
Stream
对象标识序列化的字节放在哪里BinaryFormatter
实现了IFormatter
使其知道如何序列化和反序列化对象图DeserializeFromMemory
检查流的内容,构造出所有对象的实例,并且初始化所有字段,使其与序列化前对象拥有相同的值,可以利用此特性进行深拷贝,之前List<T>
进行深拷贝就利用过这个特性private static Object DeepClone(Object original) { // 构造临时内存流 using (MemoryStream stream = new MemoryStream()) { // 构造序列化格式化器来执行所有实际工作 BinaryFormatter formatter = new BinaryFormatter(); // 值一行在本章 24.6 节“流上下文” 解释 formatter.Context = new StreamingContext(StreamingContextStates.Clone); // 将对象图序列化到内存流中 formatter.Serialize(stream, original); // 反序列化前,定位到内存流的起始位置 stream.Position = 0; // 将对象图反序列化成一组新对象, // 向调用者返回对象图(深拷贝)的根 return formatter.Deserialize(stream); } }
[Serializable] public class A { public int val; } [Serializable] public class B { public string val; } public sealed class Program { public static void Main() { var aList = new List<A>(); aList.Add(new A() { val = 1 }); aList.Add(new A() { val = 2 }); var bList = new List<B>(); bList.Add(new B() { val = "a" }); bList.Add(new B() { val = "b" }); MemoryStream ms = new MemoryStream(); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, aList); bf.Serialize(ms, bList); ms.Seek(0, SeekOrigin.Begin); var daList = (List<A>)bf.Deserialize(ms); var dbList = (List<B>)bf.Deserialize(ms); foreach (var item in daList) { Console.WriteLine(item.val); } foreach (var item in dbList) { Console.WriteLine(item.val); } // Console.WriteLine(Assembly.GetEntryAssembly().FullName); } }
MemoryStream
中,然后再将字节复制到目标字节流
,可以使用CopyTo
,CopyTo拷贝的字节是剩余字节(从Position开始算)SerializableAttribute
默认序列化枚举和委托类型
,并且是不可被派生类继承的
[NonSerialized]
将字段不序列化,减少传输量和性能[NonSerialized]
,当进行反序列化后,对象这个字段将为默认值,可以利用[OnDeserialized]
标识,将会在序列化完成后调用[NonSerialized]和[OnDeserialized]会被派生类继承(如果B继承了A,那么当B反序列化后同样会执行[OnDeserialized]标记的方法),基类被[NonSerialized]字段还是不纳入序列化
OnDeserializing、OnSerializing、OnSerialized
[Serializable] public class A { public int a; public int b; [NonSerialized] public int sum; public A(int a, int b) { this.a = a; this.b = b; this.sum = a + b; } [OnDeserialized] void OnDeserialized_ClassA(StreamingContext context) { sum = a + b; } } public sealed class Program { public static void Main() { A a = new A(1, 2); MemoryStream ms = new MemoryStream(); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, a); ms.Seek(0, SeekOrigin.Begin); A da = (A) bf.Deserialize(ms); Console.WriteLine($"Deserialize sum {da.sum}"); // Console.WriteLine("..."); } }
如何序列化对象的字段
– FormatterServices.GetSerializableMembers方法
通过反射可以获得所有可序列化字段(除了[NonSerialized]
标记的字段)
– FormatterServices.GetObjectData方法
获得被序列化的字段的值(这个和上面方法返回的MemberInfo是一一对应的)
– 格式化器将程序集标识和类型的完整名称
写入流中
– 格式化器然后遍历两个数组中的元素,将每个成员的名称和值写入流中
如何反序列化对象的字段
– 首先读取程序集标识和类型完整名称
,如果程序集没有加载到AppDomain中,那么就去加载,无法加载就抛出异常,程序集存在就使用FormatterServices.GetTypeFromAssembly
获得反序列化类型
– 然后将类型传入FormatterServices.GetUninitializedObject方法
,这个方法分配一个新对象,但是不调用构造方法,对象所有字节为Null或0
– 格式化器创建一个MemberInfo数组
,利用FormatterServices.GetSerializableMembers方法
获得,然后根据流中的数据初始化一个Object数组
– 最后利用FormatterServices.PopulateObjectMembers
一一对应填充字段
OnSerialized、OnDeserialized
不能满足对序列化全部控制,并且在上面发现序列化和反序化使用了反射
,反射速度是很慢的后面用到再看
IFormatter
的Context
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。