zoukankan      html  css  js  c++  java
  • C# 序列化理解 2(转)

    一、概述

    序列化是把对象转变成流。相反的过程就是反序列化。

    哪些场合用到这项技术呢?

    1. 把对象保存到本地,下次运行程序时恢复这个对象。

    2. 把对象传送到网络的另一台终端上,然后在此终端还原这个对象。

    3. 复制系统的粘帖板中,然后用快捷键Ctrl+V恢复这个对象。

    常用的序列化流有Binary(二进制流),XML,SOAP。

    二、序列化和反序列化使用事例:

    这里我们把序列化和反序列化以功能类的形式展现:

    复制代码
     public class Serializer
        {
            //将类型序列化为字符串
            public static string Serialize<T>(T t) where T : class
            { 
                using(MemoryStream stream=new MemoryStream())
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(stream, t);
                    return System.Text.Encoding.UTF8.GetString(stream.ToArray());
                }
            }
    
            //将类型序列化为文件
            public static void SerializeToFile<T>(T t, string path, string fullName) where T : class
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                string fullPath = string.Format(@"{0}{1}", path, fullName);
                using (FileStream stream = new FileStream(fullPath,FileMode.OpenOrCreate))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(stream, t);
                    stream.Flush();
                }
            }
    
            //将类型序列化为文件
            public static void SerializeToFileByXml<T>(T t, string path, string fullName) where T : class
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                
                string fullPath = string.Format(@"{0}{1}", path, fullName);
    
                using (FileStream stream = new FileStream(fullPath, FileMode.OpenOrCreate))
                {
                    XmlSerializer formatter = new XmlSerializer(typeof(T));
                    formatter.Serialize(stream, t);
                    stream.Flush();
                }
            }
            //将字符串反序列化为类型
            public static TResult Deserialize<TResult>(string s) where TResult : class
            {
                byte[] bs = System.Text.Encoding.UTF8.GetBytes(s);
                using (MemoryStream stream = new MemoryStream(bs))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    return formatter.Deserialize(stream) as TResult;
                }
            }
    
            //将文件反序列化为类型
            public static TResult DeserializeFromFile<TResult>(string path) where TResult : class
            {
                using (FileStream stream = new FileStream(path,FileMode.Open))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    return formatter.Deserialize(stream) as TResult;
                }
            }
    
            //将xml文件反序列化为类型
            public static TResult DeserializeFromFileByXml<TResult>(string path) where TResult : class
            {
                using (FileStream stream = new FileStream(path, FileMode.Open))
                {
                    XmlSerializer formatter = new XmlSerializer(typeof(TResult)); ;
                    return formatter.Deserialize(stream) as TResult;
                }
            }
        }
    复制代码

    上面事例中的方法是以泛型方法实现的,其中附加了泛型约束,保证泛型安全。

    序列化功能类有了下面我们建一个Book对象,用它来测试我们的功能类。

    复制代码
        [Serializable]
        public class Book
        {
            [NonSerialized]
            private string _bookPwd;
            [field: NonSerialized]
            public event EventHandler NameChanged;
            private string _bookName;
            private string _bookID;
            public ArrayList alBookReader;
            public string _bookPrice;
    
            public Book()
            {
                alBookReader = new ArrayList();
            }
    
            public string BookName
            {
                get { return _bookName; }
                set
                {
                    if (NameChanged != null)
                    {
                        NameChanged(this, null);
                    }
                    _bookName = value;
                }
            }
    
            public void BookPwd(string pwd)
            {
                 _bookPwd=pwd;
            }
           
            public string BookID
            {
                get { return _bookID; }
                set { _bookID = value; }
            }
    
            public void SetBookPrice(string price)
            {
                _bookPrice = price;
            }
    
            [OnDeserializedAttribute]
            public void changeName(StreamingContext context)
            {
                this.BookName = "C#深入浅出";
            }
    
            public void Write()
            {
                Console.WriteLine("Book ID:" + BookID);
                Console.WriteLine("Book Name:" + BookName);
                Console.WriteLine("Book Password:" + _bookPwd);
                Console.WriteLine("Book Price:" + _bookPrice);
                Console.WriteLine("Book Reader:");
                for (int i = 0; i < alBookReader.Count; i++)
                {
                    Console.WriteLine(alBookReader[i]);
                }
            }
        }
    复制代码

    关键介绍:
    1.[Serializable]特性定义该类型可以被序列化;

    2.[NonSerialized]定义某个属性不被序列化,即:内部成员被NonSerialized禁止序列化特性标记;

    3.[field: NonSerialized]定义事件不被序列化;

    4.[OnDeserializedAttribute]当它应用于某个方法时,会指定对象被反序列化后立即执行此方法。

    5.[OnDeserializingAttribute]当它应用于某个方法时,会指定对象被反序列化时立即执行此方法。

    6.[OnSerializedAttribute]如果将对象图应用于某个方法时,会指定在序列化该对象图后是否调用此方法。

    7.[OnSerializingAttribute]当它应用于某个方法时,会指定在对象序列化前调用此方法。、

    我们用控制台程序实现序列化和反序列化:

    复制代码
    static void Main(string[] args)
            {
                string path = "c:\Test\";
                Book book = new Book();
                book.NameChanged += new EventHandler(make_NameChanged);
                book.BookID = "2001";
                book.alBookReader.Add("Abel");
                book.alBookReader.Add("Tomson");
                book.BookName = "敏捷无敌";
                book.BookPwd("*****");
                book.SetBookPrice("102.00");
                //对象序列化
                Serializer.SerializeToFileByXml<Book>(book, path, "book.txt");
                //对象反序列化
                Book anothorbookserialize = new Book();
                anothorbookserialize = Serializer.DeserializeFromFileByXml<Book>(path + "book.txt");
                anothorbookserialize.Write();
                Console.ReadKey();
            }
    
            static void make_NameChanged(object sender, EventArgs e)
            {
                Console.WriteLine("Name Changed");
            }
    复制代码

    我们的事例是调用XML序列化流文件形式,序列化执行后的文件如下:

    复制代码
    <?xml version="1.0"?>
    <Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <alBookReader>
        <anyType xsi:type="xsd:string">Abel</anyType>
        <anyType xsi:type="xsd:string">Tomson</anyType>
      </alBookReader>
      <_bookPrice>102.00</_bookPrice>
      <BookName>敏捷无敌</BookName>
      <BookID>2001</BookID>
    </Book>
    复制代码

    反序列化输出如下:

    Name Changed
    Book ID:2001
    Book Name:敏捷无敌
    Book Password:
    Book Price:102.00
    Book Reader:
    Abel
    Tomson

    结果分析:

    [NonSerialized]Book Password属性在序列化XML文件中没有出现Book Password。

    [field: NonSerialized]NameChanged事件,在序列化XML文件中没有出现NameChanged。

    [OnDeserializedAttribute]changeName()方法,执行反序列化后没有立即执行changeName()方法,Book Name名称没有改变,需要通过Binary流形式才能成功执行方法。

        //序列化
        Serializer.SerializeToFile<Book>(book, path, "book.txt");
        //反序列化
        anothorbookserialize = Serializer.DeserializeFromFile<Book>(path + "book.txt");

    我们以XML流为例是为了更好的理解序列化和反序列化的执行过程,实际应用中多数以Binary流形式实现序列化和反序列化较多。

    三、继承ISerializable接口更灵活的控制序列化过程:

      当以上Serializable特性无法满足复杂的序列化过程时就需要实现ISerializable接口了。

      以下是格式化器的工作流程:如果格式化器在序列化一个对象的时候,发现对象实现了ISerializable接口,那他会忽略类所有序列化特性,转而调用GetObjectData方法的一个SerializationInfo对象,方法内部负责该对象属性的添加。反序列化时调用该对象受保护带参数构造方法中的一个SerializationInfo对象,方法内部对象属性赋值。

    下面我们实现ISerializable接口的子类型应负责父类型的序列化为例:

    1.父类同样实现了ISerializable接口

    复制代码
    [Serializable]
        public class Person:ISerializable
        {
            public string Name{get;set;}
            
            public Person()
            {
    
            }
    
            protected Person(SerializationInfo info,StreamingContext context)
            {
                Name = info.GetString("Name");
            }
    
            public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Name",Name);
            }
        }
        [Serializable]
        public class Employee: Person , ISerializable
        {
            public int Salary{get;set;}
    
    public Employee() { } protected Employee(SerializationInfo info, StreamingContext context) { Salary = info.GetInt32("Salary"); Name = info.GetString("Name"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info,context); info.AddValue("Salary", Salary); } }
    复制代码

    注意:Employee中的GetObjectData方法覆盖了基类Person中的虚方法GetObjectData。

    Employee employee = new Employee() { Name = "Abel", Salary=1220 };
    BinarySerializer.SerializeToFile<Employee>(employee, strFile, "employee.txt");
    employee = BinarySerializer.DeserializeFromFile<Employee>("c:\Test\employee.txt");
    Console.WriteLine(employee.Name);
    Console.WriteLine(employee.Salary);

    2.若父类没有实现了ISerializable接口如何处理呢?

    我们要实现继承ISerializable接口的Employee类的一个父类Person,Person没有实现ISerializable接口,序列化器没有默认去处理Person对象,只能由我们自己去做。

    下面我们用具体实例实现:

    复制代码
       [Serializable]
        public class Person
        {
            public string Name{get;set;} }
    
        [Serializable]
        public class Employee: Person , ISerializable
        {
            public int Salary{get;set;}
    
            public Employee()
            {
    
            }
    
            protected Employee(SerializationInfo info, StreamingContext context)
            {
                Salary = info.GetInt32("Salary");
                Name = info.GetString("Name");
            }
    
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Name", Name);
                info.AddValue("Salary", Salary);
            }
        }
    复制代码

    在这此序列化学习中我们用到了事件、泛型和流文件的处理知识,通过序列化我们可以实现本地加载,远程还原对象。

    得到了一个Serializer工具类,该工具类封装了序列化和反序列化的过程。

  • 相关阅读:
    BZOJ 1391: [Ceoi2008]order
    BZOJ 4504: K个串
    2019 年百度之星·程序设计大赛
    POJ 2398 Toy Storage (二分 叉积)
    POJ 2318 TOYS (二分 叉积)
    HDU 6697 Closest Pair of Segments (计算几何 暴力)
    HDU 6695 Welcome Party (贪心)
    HDU 6693 Valentine's Day (概率)
    HDU 6590 Code (判断凸包相交)
    POJ 3805 Separate Points (判断凸包相交)
  • 原文地址:https://www.cnblogs.com/seely/p/4207737.html
Copyright © 2011-2022 走看看