序列化和持久化:
描述对象状态以某种形式存储到文件,以及后来的恢复该对象状态。
Serializable属性:
默认,用户定义类型(类,结构)不可序列化,需要显示用[Serializable]特性修饰自定义类型。
不可序列化成员:
当一个类可序列化,.NET检查其下所有成员变量都可以序列化。当序列化时,.NET发现不可序列化成员会抛出异常。
对不可序列化的成员,应该用[NonSerialized]特性修饰,这样在序列化时会忽略跳过该成员。在反序列化时,会将不可序列化对象先初始化为默认值,再用自定义步骤将之初始化为正确值。
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
[Serializable]
public class MyClass : IDeserializationCallback
{
//该字段将不被序列化
[NonSerialized]
private int m_Number = 1;
#region IDeserializationCallback 成员
//在反序列化后调用,初始未序列化字段
public virtual void OnDeserialization(object sender)
{
//输出:0
Console.WriteLine(m_Number);
m_Number = 3;
//输出:3
Console.WriteLine(m_Number);
}
#endregion
}
static void Main(string[] args)
{
#region 序列化
//MyClass mc = new MyClass();
//IFormatter formatter = new BinaryFormatter();
//Stream stream = new FileStream(@"D:\66_Code\Se\obj.bin", FileMode.Create, FileAccess.Write);
//using (stream)
//{
// formatter.Serialize(stream, mc);
//}
#endregion
#region 反序列化
MyClass obj;
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(@"D:\66_Code\Se\obj.bin", FileMode.Open, FileAccess.Read);
using (stream)
{
obj = (MyClass)formatter.Deserialize(stream);
}
#endregion
Console.Read();
}
委托和序列化:
//委托成员变量应标记为不可序列化
[NonSerializable]
private EventHandler m_MyEvent;
//对于事件,要加上field属性限定
[field:NonSerializable]
private event EventHandler m_MyEvent;
序列化格式器:
格式器是一个实现 IFormatter接口的对象。
1.二进制格式器(BinaryFormatter)
2.SOAP格式器(SoapFormatter)
using System.Runtime.Serialization.Formatters.Soap;
#region 序列化
//MyClass mc = new MyClass();
//IFormatter formatter = new SoapFormatter();
//Stream stream = new FileStream(@"D:\66_Code\Se\obj.xml", FileMode.Create, FileAccess.Write);
//using (stream)
//{
// formatter.Serialize(stream, mc);
//}
#endregion
#region 反序列化
MyClass obj;
IFormatter formatter = new SoapFormatter();
Stream stream = new FileStream(@"D:\66_Code\Se\obj.xml", FileMode.Open, FileAccess.Read);
using (stream)
{
obj = (MyClass)formatter.Deserialize(stream);
}
#endregion
XML序列化(XmlSerializer):
为了面向因特网与其他平台交换数据。.NET序列化基于组件,面向应用程序和交互对象。
using System.Xml.Serialization;
#region 序列化
//MyClass mc = new MyClass();
//XmlSerializer xml = new XmlSerializer(typeof(MyClass));
//Stream stream = new FileStream(@"D:\66_Code\Se\obj.xml", FileMode.Create, FileAccess.Write);
//using (stream)
//{
// xml.Serialize(stream, mc);
//}
#endregion
#region 反序列化
MyClass obj;
XmlSerializer xml = new XmlSerializer(typeof(MyClass));
Stream stream = new FileStream(@"D:\66_Code\Se\obj.xml", FileMode.Open, FileAccess.Read);
using (stream)
{
obj = (MyClass)xml.Deserialize(stream);
}
#endregion
序列化事件:
当序列化或反序列化发生时,.NET支持在类上调用自定义方法处理。
[Serializable]
public class MyClass
{
//序列化事件(当前类必须标记为可序列化,且只有BinaryFormatter,SoapFormatter调用序列化,反序列化才有效)
//除了.NET应没有其他程序能调用序列化事件,建议设为私有
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
Console.WriteLine("在序列化开始前调用");
}
[OnSerialized]
private void OnSerialized(StreamingContext context)
{
Console.WriteLine("在序列化后调用");
}
[OnDeserializing]
private void OnDeserializing(StreamingContext context)
{
Console.WriteLine("在反序列化前调用");
}
//可代替OnDeserialization()方法,初始化未序列化字段
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
Console.WriteLine("在反序列化后及OnDeserialization()方法后调用");
}
}
克隆可序列化对象(利用类存流):
//克隆可序列化对象
public static T Clone<T>(T obj)
{
//检查当前类型是否可序列化
if (typeof(T).IsSerializable)
{
IFormatter formatter = new BinaryFormatter();
//将对象,序列化到内存流中保存再反序列化,完成深层次克隆对象
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, obj);
stream.Seek(0, SeekOrigin.Begin);
T clone = (T)formatter.Deserialize(stream);
return clone;
}
}
throw new Exception();
}
序列化多个对象:
[Serializable]
public class MyClass
{
}
[Serializable]
public class MyOtherClass
{
}
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
MyOtherClass moc = new MyOtherClass();
IFormatter formatter = new BinaryFormatter();
//将不同对象使用同一流序列化的同一文件中
Stream stream = new FileStream(@"D:\66_Code\Se\All.bin", FileMode.Create, FileAccess.Write);
using (stream)
{
formatter.Serialize(stream, mc);
formatter.Serialize(stream, moc);
}
mc = null;
moc = null;
//从同一文件反序列化为不同对象(注意顺序!)
stream = new FileStream(@"D:\66_Code\Se\All.bin", FileMode.Open, FileAccess.Read);
using (stream)
{
mc = (MyClass)formatter.Deserialize(stream);
moc = (MyOtherClass)formatter.Deserialize(stream);
}
Console.Read();
}
}
自定义序列化(通过实现ISerializable接口):
//实现ISerializable接口的类必用Serializable特性修饰,否则.NET将忽略该接口
[Serializable]
public class MyClass<T> : ISerializable
{
private string m_String = "LULU";
//此处无法保证T的类型是可序列化的
private T m_Value;
#region 构造函数
//为支持相匹配自定义反序列化,必须提供一反序列化构造函数
//应将该构函始终定义为受保护,防止客户端调用
protected MyClass(SerializationInfo info, StreamingContext context)
{
//反序列化时,将加密后的值解密还原为正确值
m_String = info.GetString("m_String").ToUpper();
//泛型
m_Value = (T)info.GetValue("m_Value", typeof(T));
}
//定义静态构造函数,实现在使用该类前,执行一次运行时检查
//可用此技巧进行其他约束性检查
static MyClass()
{
if(typeof(T).IsSerializable == false)
{
throw new SerializationException("类型不可序列化!");
}
}
public MyClass()
{ }
#endregion
#region ISerializable 成员
//序列化对象时,.NET会先检查是否可序列化,再检查是否实现ISerializable接口
//实现该接口,就调用GetObjectData()获取对象状态
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
//可以按照自定义方式提供值(实现加密之类,避免暴露真实值)
info.AddValue("m_String", m_String.ToLower());
//泛型
info.AddValue("m_Value", m_Value, typeof(T));
}
#endregion
}
序列化和类层次结构:
对一个类使用Serializable特性,只影响该类,不会导致派生类可序列化。