序列化通俗地讲就是将一个对象转换成一个字节流的过程,这样就可以轻松保存在磁盘文件或数据库中。反序列化是序列化的逆过程,就是将一个字节流转换回原来的对象的过程。
然而为什么需要序列化和反序列化这样的机制呢?这个问题也就涉及到序列化和反序列化的用途了,
对于序列化的主要用途有:
1)、将应用程序的状态保存在一个磁盘文件或数据库中,并在应用程序下次运行时恢复状态。例如, Asp.net 中利用序列化和反2)、序列化来保存和恢复会话状态。
3)、一组对象可以轻松复制到Windows 窗体的剪贴板中,再粘贴回同一个或者另一个应用程序。
将对象按值从一个应用程序域中发送到另一个程序域
并且如果把对象序列化成内存中的字节流,就可以利用一些其他的技术来处理数据,例如,对数据进行加密和压缩等。
序列化和反序列化的简单使用:
using
System;
using
System.IO;
using
System.Runtime.Serialization.Formatters.Binary;
namespace
Serializable
{
[Serializable]
public
class
Person
{
public
string
personName;
[NonSerialized]
public
string
personHeight;
private
int
personAge;
public
int
PersonAge
{
get
{
return
personAge; }
set
{ personAge = value; }
}
public
void
Write()
{
Console.WriteLine(
"Person Name: "
+personName);
Console.WriteLine(
"Person Height: "
+personHeight);
Console.WriteLine(
"Person Age: "
+ personAge);
}
}
class
Program
{
static
void
Main(
string
[] args)
{
Person person =
new
Person();
person.personName =
"Jerry"
;
person.personHeight =
"175CM"
;
person.PersonAge = 22;
Stream stream = Serialize(person);
//为了演示,都重置
stream.Position = 0;
person =
null
;
person = Deserialize(stream);
person.Write();
Console.Read();
}
private
static
MemoryStream Serialize(Person person)
{
MemoryStream stream =
new
MemoryStream();
// 构造二进制序列化格式器
BinaryFormatter binaryFormatter =
new
BinaryFormatter();
// 告诉序列化器将对象序列化到一个流中
binaryFormatter.Serialize(stream, person);
return
stream;
}
private
static
Person Deserialize(Stream stream)
{
BinaryFormatter binaryFormatter =
new
BinaryFormatter();
return
(Person)binaryFormatter.Deserialize(stream);
}
}
}
从中可以看出除了标记NonSerialized的其他成员都能序列化,注意这个属性只能应用于一个类型中的字段,而且会被派生类型继承。
SOAP 和XML 的序列化和反序列化和上面类似,只需要改下格式化器就可以了, 这里我就不列出来了。
三、控制序列化和反序列化
有两种方式来实现控制序列化和反序列化:
通过OnSerializing, OnSerialized,OnDeserializing, OnDeserialized,NonSerialized和OptionalField等属性
实现System.Runtime.Serialization.ISerializable接口
第一种方式实现控制序列化和反序列化代码:
using
System;
using
System.IO;
using
System.Runtime.Serialization;
using
System.Runtime.Serialization.Formatters.Binary;
namespace
ControlSerialization
{
[Serializable]
public
class
Circle
{
private
double
radius;
//半径
[NonSerialized]
public
double
area;
//面积
public
Circle(
double
inputradiu)
{
radius = inputradiu;
area = Math.PI * radius * radius;
}
[OnDeserialized]
private
void
OnDeserialized(StreamingContext context)
{
area = Math.PI * radius * radius;
}
public
void
Write()
{
Console.WriteLine(
"Radius is: "
+ radius);
Console.WriteLine(
"Area is: "
+ area);
}
}
class
Program
{
static
void
Main(
string
[] args)
{
Circle c =
new
Circle(10);
MemoryStream stream =
new
MemoryStream();
BinaryFormatter formatter =
new
BinaryFormatter();
// 将对象序列化到内存流中,这里可以使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
formatter.Serialize(stream,c);
stream.Position = 0;
c =
null
;
c = (Circle)formatter.Deserialize(stream);
c.Write();
Console.Read();
}
}
}
注意:如果注释掉 OnDeserialized属性的话,area字段的值就是0了,因为area字段没有被序列化到流中。
在上面需要序列化的对象中,格式化器只会序列化对象的radius字段的值。area字段中的值不会序列化,因为该字段已经应用了NonSerializedAttribute属性,然后我们用Circle c=new Circle(10)这样代码构建一个Circle对象时,在内部,area会设置一个约为314.159这样的值,这个对象序列化时,只有radius的字段的值(10)写入流中, 但当反序列化成一个Circle对象时,它的area字段的值会初始化为0,而不是约314.159的一个值。为了解决这样的问题,所以自定义一个方法应用OnDeserializedAttribute属性。此时的执行过程为:每次反序列化类型的一个实例,格式化器都会检查类型中是否定义了 一个应用了该attribute的方法,如果是,就调用该方法,调用该方法时,所有可序列化的字段都会被正确设置。除了OnDeserializedAttribute这个定制attribute,system.Runtime.Serialization命名空间还定义了OnSerializingAttribute,OnSerializedAttribute和OnDeserializingAttribute这些定制属性。
实现ISerializable接口方式控制序列化和反序列化代码:
using
System;
using
System.IO;
using
System.Runtime.Serialization;
using
System.Runtime.Serialization.Formatters.Binary;
using
System.Security.Permissions;
namespace
ControlSerilization2
{
[Serializable]
public
class
MyObject : ISerializable
{
public
int
n1;
public
intn2;
[NonSerialized]
public
String str;
public
MyObject()
{
}
protected
MyObject(SerializationInfo info, StreamingContext context)
{
n1 = info.GetInt32(
"i"
);
n2 = info.GetInt32(
"j"
);
str = info.GetString(
"k"
);
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter =
true
)]
public
virtual
void
GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(
"i"
, n1);
info.AddValue(
"j"
, n2);
info.AddValue(
"k"
, str);
}
public
void
Write()
{
Console.WriteLine(
"n1 is: "
+ n1);
Console.WriteLine(
"n2 is: "
+ n2);
Console.WriteLine(
"str is: "
+ str);
}
}
class
Program
{
static
void
Main(
string
[] args)
{
MyObject obj =
new
MyObject();
obj.n1 = 2;
obj.n2 = 3;
obj.str =
"Jeffy"
;
MemoryStream stream =
new
MemoryStream();
BinaryFormatter formatter =
new
BinaryFormatter();
// 将对象序列化到内存流中,这里可以使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
formatter.Serialize(stream, obj);
stream.Position = 0;
obj =
null
;
obj = (MyObject)formatter.Deserialize(stream);
obj.Write();
Console.Read();
}
}
}
四、格式化器如何序列化和反序列化
从上面的分析中可以看出,进行序列化和反序列化主要是格式化器在工作的,然而下面就是要讲讲格式化器是如何序列化一个应用了 SerializableAttribute 属性的对象。
1、格式化器调用FormatterServices的GetSerializableMembers方法:public static MemberInfo[] GetSerializableMembers(Type type,StreamingContext context);这个方法利用发射获取类型的public和private实现字段(标记了NonSerializedAttributee属性的字段除外)。方法返回由MemberInfo对象构成的一个数组,其中每个元素对应于一个可序列化的实例字段。
2、对象被序列化,System.Reflection.MemberInfo对象数组传给FormatterServices的静态方法GetObjectData: public static object[] GetObjectData(Object obj,MemberInfo[] members); 这个方法返回一个Object数组,其中每个元素都标识了被序列化的那个对象中的一个字段的值。
3、格式化器将程序集标识和类型的完整名称写入流中。
4、格式化器然后遍历两个数组中的元素,将每个成员的名称和值写入流中。
接下来是解释格式化器如何自动反序列化一个应用了 SerializableAttribute属性的对象。
1、格式化器从流中读取程序集标识和完整类型名称。
2、格式化器调用FormatterServices的静态方法GetUninitializedObject: public static Object GetUninitializedObject(Type ttype);这个方法为一个新对象分配内存,但不为对象调用构造器。然而,对象的所有字段都被初始化为0或null.
3格式化器现在构造并初始化一个MemberInfo数组,调用FormatterServices的GetSerializableMembers方法,这个方法返回序列化好、现在需要反序列化的一组字段。
4、格式化器根据流中包含的数据创建并初始化一个Object数组。
5、将对新分配的对象、MemberInfo数组以及并行Object数组的引用传给FormatterServices的静态方法PopulateObjectMembers:
public static Object PopulateObjectMembers(Object obj,MemberInfo[] members,Object[] data);这个方法遍历数组,将每个字段初始化成对应的值。
注:格式化如何序列化和反序列对象部分摘自CLR via C#(第三版),写在这里可以让初学者进一步理解格式化器在序列化和反序列化过程中所做的工作。
写到这里这篇关于序列化和反序列的文章终于结束了, 希望对朋友有帮助。