序列化与反序列技术

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版

posted @ 2021-03-02 16:39  _MrZhu  阅读(71)  评论(0)    收藏  举报