序列化与反序列技术
C#里面有很多东西特别容易混用、而且记不住、总是模模糊糊、叫不准,一用就需要百度,特别蛋疼,抽空整理下易忘、混用的知识点,以儆效尤;
1.快速开始入门
#region 24.1快速开始入门 static void test1() { List<string> list = new List<string>() { "zhangsan", "lisi", "wangwu" }; MemoryStream stream = (MemoryStream)SerializeToMemory(list); stream.Seek(0, SeekOrigin.Begin); list = (List<string>)DSerializeFromMemory(stream); foreach (var s in list) { Console.WriteLine(s); } //深复制 List<string> copyList = (List<string>)DeepClone(list); Console.WriteLine(Object.ReferenceEquals(copyList, list)); Console.ReadKey(); } private static Stream SerializeToMemory(object obj) { MemoryStream ms = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); return ms; } private static Object DSerializeFromMemory(Stream stream) { BinaryFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(stream); } /// <summary> /// 序列化实现深拷贝 /// </summary> /// <param name="original"></param> /// <returns></returns> private static Object DeepClone(object original) { MemoryStream ms = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Context = new StreamingContext(StreamingContextStates.Clone); formatter.Serialize(ms, original); ms.Position = 0; return formatter.Deserialize(ms); } #endregion
2.使类型可序列化
[SerializableAttribute]只能应用于引用类型、值类型、枚举、委托,而且枚举、委托类型总是可序列化的无须显示声明;
基类可序列化,但是派生类没有显示标注可序列化,派生类没有可序列能力;反之,派生类可序列化,基类必然可序列化;
#region 24.2使类型可序列化 //SerializableAttribute只能应用于引用类型、值类型、枚举、委托,而且枚举、委托类型总是可序列化的无须显示声明; [Serializable] internal class Person { //可序列化 } internal class LowPerson : Person { //不可序列化 } static void test2() { try { MemoryStream stream = new MemoryStream(); Person person = new Person(); stream = (MemoryStream)SerializeToMemory(person); Console.WriteLine("person 可序列化"); LowPerson lowperson = new LowPerson(); stream = (MemoryStream)SerializeToMemory(lowperson); } catch (SerializationException ex) { Console.WriteLine(ex.Message); Console.ReadKey(); } } #endregion
3.控制系列与反序列化
一般有2个原因使我们有些字段不想被序列化:
1.字段含有反序列化后无效的信息。如包含windows内核对象(文件、进程、线程、事件、互斥体、信号量等)的句柄,那么反序列化后到其他进程或其他机器后就会失去字段意义。
2.比较容易计算出来的字段信息,不需要被序列化传输。
可序列化类下面标注[Serializable]字段可不被序列化;如果序列化之前的对象(如已保存在文件流中)没有这个字段,而在反序列化时对象新加了字段m_OptionalField,那么新加的字段要打标签OptionalField,以免发生异常;
#region 24.3控制系列与反序列化 //一般有2个原因使我们有些字段不想被序列化: //1.字段含有反序列化后无效的信息。如包含windows内核对象(文件、进程、线程、事件、互斥体、信号量等)的句柄, //那么反序列化后到其他进程或其他机器后就会失去字段意义。 //2.比较容易计算出来的字段信息,不需要被序列化传输。 [Serializable] internal class Circle { private double m_radius;//半径 [NonSerialized] private double m_area;//面积 //如果序列化之前的对象没有这个字段(流保存在文件里面),在反序列化时新加的字段要打标签NonSerialized //[OptionalField] //private double m_OptionalField; public Circle(double radius) { this.m_radius = radius; this.m_area = Math.PI * radius * radius; } //反序列化后执行 [OnDeserialized] public void OnDeserialized(StreamingContext context) { m_area = Math.PI * m_radius * m_radius; } } static void Test3() { Circle circle = new Circle(2); MemoryStream stream = new MemoryStream(); stream = (MemoryStream)SerializeToMemory(circle); FileStream fs = new FileStream("test.txt", FileMode.Create); BinaryWriter binaryWriter = new BinaryWriter(fs); binaryWriter.Write(stream.ToArray()); binaryWriter.Close(); fs.Close(); stream.Close(); //反序列化 FileStream fs2 = new FileStream("test.txt", FileMode.Open); fs2.Position = 0; circle = (Circle)DSerializeFromMemory(fs2); Console.ReadKey(); } #endregion
温故而知新,牢记使命,不忘初心,方的始终!
4.自己实现ISerializable接口的类
[Serializable] public class MyItemType : ISerializable { public MyItemType() { // Empty constructor required to compile. } // The value to serialize. private string myProperty_value; public string MyProperty { get { return myProperty_value; } set { myProperty_value = value; } } // Implement this method to serialize data. The method is called // on serialization. public void GetObjectData(SerializationInfo info, StreamingContext context) { // Use the AddValue method to specify serialized values. info.AddValue("props", myProperty_value, typeof(string)); } // The special constructor is used to deserialize values. public MyItemType(SerializationInfo info, StreamingContext context) { // Reset the property value using the GetValue method. myProperty_value = (string)info.GetValue("props", typeof(string)); // myProperty_value2 = (string)info.GetValue("props2", typeof(string)); } } // This is a console application. public static class Test { public static void Main1() { // This is the name of the file holding the data. You can use any file extension you like. string fileName = "dataStuff.myData"; // Use a BinaryFormatter or SoapFormatter. IFormatter formatter = new BinaryFormatter(); //IFormatter formatter = new SoapFormatter(); Test.SerializeItem(fileName, formatter); // Serialize an instance of the class. Test.DeserializeItem(fileName, formatter); // Deserialize the instance. Console.WriteLine("Done"); Console.ReadLine(); } public static void SerializeItem(string fileName, IFormatter formatter) { // Create an instance of the type and serialize it. MyItemType t = new MyItemType(); t.MyProperty = "Hello World"; FileStream s = new FileStream(fileName, FileMode.Create); formatter.Serialize(s, t); s.Close(); } public static void DeserializeItem(string fileName, IFormatter formatter) { FileStream s = new FileStream(fileName, FileMode.Open); MyItemType t = (MyItemType)formatter.Deserialize(s); Console.WriteLine(t.MyProperty); } }
5.序列化成不同的对象,反序列化成不同的对象
如:对应在一个应用程序域中只能有一个单件对象的情况,需要经过序列化与反序列操作时,就要保证操作后的应用程序域中还是只有一个单件对象;
Singleton[] array=Singleton[2]是指向同一对象的数组,在反序列化后,应该依然还是指向同一对象才合理;
[Serializable] public class Singleton : ISerializable { private Singleton() { } private static readonly Singleton _singleton = new Singleton(); public string Name = "test"; public static Singleton GetSingleton() { return _singleton; } [SecurityPermission(SecurityAction.Demand,SerializationFormatter=true)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.SetType(typeof(SingletonSerilizationHelper)); } [Serializable] private sealed class SingletonSerilizationHelper:IObjectReference { public object GetRealObject(StreamingContext context) { return Singleton.GetSingleton(); } } }
在序列化时,格式化器检测出Singleton 实现了接口ISerializable,所以会调用GetObjectData()方法,这个方法调用SetType(),向它传递SingletonSerilizationHelper类型,告诉格式化器将singleton序列化成一个SingletonSerilizationHelper对象;
反序列化时,格式化器尝试反序列化一个SingletonSerilizationHelper对象,正是之前格式化器被“欺骗”的所序列化的东西,构造好对象后,格式化器发现SingletonSerilizationHelper实现了接口IObjectReference,格式化器便会调用
GetRealObject(StreamingContext context)这个方法,这个方法返回序列化好后你真正想引用的对象。
参考书籍:CLR via C#第4版
C#高级编程第4版

浙公网安备 33010602011771号