引言
程序员在编写应用程序的时候往往要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将程序数据转化成能被存储并传输的格式的过程被称为”序列化”(Serialization),而它的逆过程则可被称为”反序列化”(Deserialization)。通俗上讲,假如程序在运行的时候,内存中有一个对象,如果你想把这个对象的某些信息或者所有信息保存在本地,下次程序打开后,能够直接还原这个对象,怎么才能做到呢?
.Net框架对序列化机制具有非常好的支持,常用的为
二进制格式器(Binary Formatter) 、 XML格式器(XML Formatter)
不过在实际的应用中,二进制格式器往往应用于一般的桌面程序和网络通讯程序中,而XML格式器禀承了XML技术的优点,大多数被应用于.Net Remoting和XML Web服务等领域。下面我们来分析一下两种格式器各自的优点。
xml序列化
使用XML序列化或反序列化时,需要对XML序列化器指定需要序列化对象的类型和其关联的类型。
XML序列化只能序列化对象的公有属性,并且要求对象有一个无参的构造方法,否者无法反序列化。
[Serializable]和[NonSerialized]特性对XML序列化无效!所以使用XML序列化时不需要对对象增加[Serializable]特性。
public class Person
{
public string Name;//姓名
public bool Sex;//性别,是否是男
public Person() { }//必须提供无参构造器,否则XmlSerializer将出错
public Person(string name, bool sex)
{
this.Name = name;
this.Sex = sex;
}
public override string ToString()
{
return "姓名:" + this.Name + "\t性别:" + (this.Sex ? "男" : "女");
}
/// <summary>
/// 对象序列化成 XML String
/// </summary>
public string XmlSerialize<T>(T obj)
{
string xmlString = string.Empty;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
xmlSerializer.Serialize(ms, obj);
xmlString = Encoding.UTF8.GetString(ms.ToArray());
}
return xmlString;
}
// <summary>
/// XML String 反序列化成对象
/// </summary>
public T XmlDeserialize<T>(string xmlString)
{
T t = default(T);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
//此处用的是MemoryStream
using (Stream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString)))
{
using (XmlReader xmlReader = XmlReader.Create(xmlStream))
{
Object obj = xmlSerializer.Deserialize(xmlReader);
t = (T)obj;
}
}
return t;
}
二进制序列化
使用二进制序列化,必须为每一个要序列化的的类和其关联的类加上[Serializable]特性,对类中不需要序列化的成员可以使用[NonSerialized]特性。
二进制序列化对象时,能序列化类的所有成员(包括私有的),且不需要类有无参数的构造方法。
使用二进制格式序列化时,它不仅是将对象的字段数据进行持久化,也持久化每个类型的完全限定名称和定义程序集的完整名称(包括包称、版本、公钥标记、区域性),这些数据使得在进行二进制格式反序列化时亦会进行类型检查。所以反序列化时的运行环境要与序列化时的运行环境要相同,要记得添加引用,否者可能会无法反序列化成功。
[Serializable] //特性,当为某个类加了该特性后,这个类就标记为了可序列化的了。
public class Person : Animal
{
private Car _myCar;
public Car MyCar
{
get { return _myCar; }
set { _myCar = value; }
}
//[NonSerialized]//当前这个字段就不会被序列化了。
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
二进制序列化要求:
1.被序列化的对象的类型必须标记为可序列化的。
2.二进制序列化会把属性对应的字段序列化到文件中,所以最好类型中不要使用自动属性(编译器会自动生成字段),而要自己写属性与字段
3.当序列化一个对象的时候,这个对象本身以及的所有父类都必须标记为可序列化的。
4.类型中所有属性与字段的类型必须也标记为可序列化的。接口不需要。
5.通过[NonSerialized]把某个字段标记为不可序列化的。
Person person = new Person();
person.Name = "张三";
person.Age = 19;
person.Email = "zhangsan@yahoo.com";
person.MyCar = new Car() { Brand = "时风" };
//二进制序列化步骤:
//1.创建二进制序列化器
BinaryFormatter bf = new BinaryFormatter();
//我们希望将对象序列化到一个文件上,所以要创建一个文件流
using (FileStream fsWrite = File.OpenWrite("person.bin"))
{
//开始执行二进制序列化
bf.Serialize(fsWrite, person);
}
Console.WriteLine("序列化完毕!");
Console.ReadKey();
反序列化的时候,需要创建原来被序列化的类型的对象,所以反序列化的时候需要引用被序列化的类型的对象。正是因为如此,所以在二进制序列化的时候,那些属性名、方法等根本不需要序列化,只需要把那些状态信息(数据→字段的值)序列化了就好了,对于那些方法等信息,在反序列化时,创建对象的时候会自动创建,然后根据序列化文件中的字段的值,进行赋值。