当前位置: 代码迷 >> C# >> C# 序列化了解 2(转)
  详细解决方案

C# 序列化了解 2(转)

热度:199   发布时间:2016-05-05 05:16:47.0
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工具类,该工具类封装了序列化和反序列化的过程。

 

  相关解决方案