简介
序列化是指将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。
在面向对象的环境中实现序列化机制时,必须在易用性和灵活性之间进行一些权衡。只要您对此过程有足够的控制能力,就可以使该过程在很大程度上自动进行。例如,简单的二进制序列化不能满足需要,或者,由于特定原因需要确定类中那些字段需要序列化。以下各部分将探讨 .NET 框架提供的可靠的序列化机制,并着重介绍使您可以根据需要自定义序列化过程的一些重要功能。
持久存储
我们经常需要将对象的字段值保存到磁盘中,并在以后检索此数据。尽管不使用序列化也能完成这项工作,但这种方法通常很繁琐而且容易出错,并且在需要跟踪对象的层次结构时,会变得越来越复杂。可以想象一下编写包含大量对象的大型业务应用程序的情形,程序员不得不为每一个对象编写代码,以便将字段和属性保存至磁盘以及从磁盘还原这些字段和属性。序列化提供了轻松实现这个目标的快捷方法。
公共语言运行时 (CLR) 管理对象在内存中的分布,.NET 框架则通过使用反射提供自动的序列化机制。对象序列化后,类的名称、程序集以及类实例的所有数据成员均被写入存储媒体中。对象通常用成员变量来存储对其他实例的引用。类序列化后,序列化引擎将跟踪所有已序列化的引用对象,以确保同一对象不被序列化多次。.NET 框架所提供的序列化体系结构可以自动正确处理对象图表和循环引用。对对象图表的唯一要求是,由正在进行序列化的对象所引用的所有对象都必须标记为 Serializable(请参阅基本序列化)。否则,当序列化程序试图序列化未标记的对象时将会出现异常。
当反序列化已序列化的类时,将重新创建该类,并自动还原所有数据成员的值。
按值封送
对象仅在创建对象的应用程序域中有效。除非对象是从 MarshalByRefObject 派生得到或标记为 Serializable,否则,任何将对象作为参数传递或将其作为结果返回的尝试都将失败。如果对象标记为 Serializable,则该对象将被自动序列化,并从一个应用程序域传输至另一个应用程序域,然后进行反序列化,从而在第二个应用程序域中产生出该对象的一个精确副本。此过程通常称为按值封送。
如果对象是从 MarshalByRefObject 派生得到,则从一个应用程序域传递至另一个应用程序域的是对象引用,而不是对象本身。也可以将从 MarshalByRefObject 派生得到的对象标记为 Serializable。远程使用此对象时,负责进行序列化并已预先配置为 SurrogateSelector 的格式化程序将控制序列化过程,并用一个代理替换所有从 MarshalByRefObject 派生得到的对象。
序列化代码
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary; //二进制序列化的名空间
using System.Runtime.Serialization.Formatters.Soap; //Soap的方式序列化,需要在工程里添加System.Runtime.Serialization.Formatters.Soap.dll的引用
using System.Xml.Serialization; //XML的方式序列化
using System.IO;
namespace ObjectSerializable
{
class Program
{
static void Main(string[] args)
{
Book book = new Book();
book.BID = "B0002";
book.BookName = "C#程序设计";
book.Price = 123.98M;
Console.WriteLine(book);
Console.ReadKey();
#region 二进制序列化,特点是速度快,private对象也会序列化,当使用NonSerialized才不会序列化
Console.WriteLine("=========Binary Object Serializable=========");
BinaryFormatter formatter = new BinaryFormatter();
using(Stream fileStream = new FileStream(AppDomain .CurrentDomain.BaseDirectory + @"\BinaryObjectSerializable.xml", FileMode.Create, FileAccess.Write, FileShare.None))
{
formatter.Serialize(fileStream, book);
fileStream.Close();
}
BinaryFormatter bf = new BinaryFormatter();
using (Stream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + @"\BinaryObjectSerializable.xml", FileMode.Open, FileAccess.Read, FileShare.None))
{
Book bk = (Book)bf.Deserialize(fs);
fs.Close();
Console.WriteLine(bk);
Console.ReadKey();
}
#endregion
#region Soap序列化方式
Console.WriteLine("=========Soap Object Serializable=========");
//功能和二进制序列化类似
SoapFormatter soapFormatter = new SoapFormatter();
using (Stream streamFile = new FileStream(AppDomain.CurrentDomain.BaseDirectory + @"\SoapObjectSerializable.xml", FileMode.Create, FileAccess.Write, FileShare.None))
{
soapFormatter.Serialize(streamFile, book);
streamFile.Close();
}
using (Stream streamFile = new FileStream(AppDomain.CurrentDomain.BaseDirectory + @"\SoapObjectSerializable.xml", FileMode.Open, FileAccess.Read, FileShare.None))
{
Book bb = (Book)soapFormatter.Deserialize(streamFile);
streamFile.Close();
Console.WriteLine(bb);
}
Console.ReadKey();
#endregion;
Console.WriteLine ("==========XML Object Serializeable=============");
//private标记的字段不会被序列化
//使用XmlIgnore 可以忽略序列化的属性
//XML反序列化速度比较慢
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Book));
using (Stream streamFile = new FileStream(AppDomain.CurrentDomain.BaseDirectory + @"\XMLObjectSerializable.xml", FileMode.Create, FileAccess.Write, FileShare.None))
{
xmlSerializer.Serialize(streamFile, book);
streamFile.Close();
}
using (Stream streamFile = new FileStream(AppDomain.CurrentDomain.BaseDirectory + @"\XMLObjectSerializable.xml", FileMode.Open, FileAccess.Read, FileShare.None))
{
Book bxml = (Book)xmlSerializer.Deserialize(streamFile);
streamFile.Close();
Console.WriteLine(bxml);
}
Console.ReadKey();
}
}
}
类代码
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml .Serialization;
namespace ObjectSerializable
{
[Serializable]
public class Book
{
//[NonSerialized] //只能用于字段 //XML序列化是不影响
private string bid;
private string bookName;
private decimal price;
//[XmlIgnore] //只对XML序列化有影响
public string BID
{
get { return bid; }
set { this.bid = value; }
}
public string BookName
{
get { return bookName; }
set { this.bookName = value; }
}
public decimal Price
{
get { return price; }
set { this.price = value; }
}
public override string ToString()
{
return string.Format("bid:{0} BookName:{1} Price:{2:C}",this.bid,this.bookName,this.price );
}
}
}
总结:
1:要序列化一个类时用 [Serializable] 标记;
2:二进制序列化速度比较快,私有字段也可以序列化,如果不想序列化某个字段可以用 [NonSerialized] 标记,这个binary和soap序列化方式有影响;
3:XML序列化时只序列化公有字段,私有的不序列化,要使某个属性不被序列化可以使用 [XmlIgnore]标记;
4:binary和soap序列化比较相同,xml序列化速度比较慢;
5:用到的几个名空间:
using System.Runtime.Serialization.Formatters.Binary; //二进制序列化的名空间
using System.Runtime.Serialization.Formatters.Soap; //Soap的方式序列化,需要在工程里添加System.Runtime.Serialization.Formatters.Soap.dll的引用
using System.Xml.Serialization;