您的位置:首页 > 其它

谈谈:.Net中的序列化和反序列化

2012-07-18 10:23 351 查看
转自:/article/4747977.html

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,OnSerializedAttributeOnDeserializingAttribute这些定制属性。

实现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();
}
}
}


结果为:



此时的执行过程为:当格式化器序列化对象时,会检查每个对象,如果发现一个对象的类型实现了ISerializable接口,格式化器会忽视所有定制属性,改为构造一个新的System.Runtime.Serialization.SerializationInfo对象,这个对象包含了要实际为对象序列化的值的集合。构造好并初始化好SerializationInfo对象后,格式化器调用类型的GetObjectData方法,并向它传递对SerializationInfo对象的引用,GetObjectData方法负责决定需要哪些信息来序列化对象,并将这些信息添加到SerializationInfo对象中,通过调用AddValue方法来添加需要的每个数据,添加好所有必要的序列化信息后,会返回至格式化器,然后格式化器获取已经添加到SerializationInfo对象中的所有值,并将它们都序列化到流中,当反序列化时,格式化器从流中提取一个对象时,会为新对象分配内存,最初,这个对象的所有字段都设为0或null,然后,格式化器检查类型是否实现了ISerializable接口,如果存在这个接口,
格式化器就尝试调用一个特殊构造器,它的参数和GetObjectData方法的完全一致。

四、格式化器如何序列化和反序列化

从上面的分析中可以看出,进行序列化和反序列化主要是格式化器在工作的,然而下面就是要讲讲格式化器是如何序列化一个应用了 SerializableAttribute 属性的对象。

格式化器调用FormatterServicesGetSerializableMembers方法:public static MemberInfo[] GetSerializableMembers(Type type,StreamingContext context);这个方法利用发射获取类型的public和private实现字段(标记了NonSerializedAttributee属性的字段除外)。方法返回由MemberInfo对象构成的一个数组,其中每个元素对应于一个可序列化的实例字段。

对象被序列化,System.Reflection.MemberInfo对象数组传给FormatterServices的静态方法GetObjectData: public static object[] GetObjectData(Object obj,MemberInfo[] members); 这个方法返回一个Object数组,其中每个元素都标识了被序列化的那个对象中的一个字段的值。

格式化器将程序集标识和类型的完整名称写入流中。
格式化器然后遍历两个数组中的元素,将每个成员的名称和值写入流中。

接下来是解释格式化器如何自动反序列化一个应用了 SerializableAttribute属性的对象。

格式化器从流中读取程序集标识和完整类型名称。
格式化器调用FormatterServices的静态方法GetUninitializedObject: public static Object GetUninitializedObject(Type ttype);这个方法为一个新对象分配内存,但不为对象调用构造器。然而,对象的所有字段都被初始化为0或null.

格式化器现在构造并初始化一个MemberInfo数组,调用FormatterServicesGetSerializableMembers方法,这个方法返回序列化好、现在需要反序列化的一组字段。

格式化器根据流中包含的数据创建并初始化一个Object数组。
将对新分配的对象、MemberInfo数组以及并行Object数组的引用传给FormatterServices的静态方法PopulateObjectMembers:

public static Object PopulateObjectMembers(Object obj,MemberInfo[] members,Object[] data);这个方法遍历数组,将每个字段初始化成对应的值。

注:格式化如何序列化和反序列对象部分摘自CLR via C#(第三版),写在这里可以让初学者进一步理解格式化器在序列化和反序列化过程中所做的工作。

写到这里这篇关于序列化和反序列的文章终于结束了, 希望对自己以后复习和园子里的朋友有帮助。

作者:Learnig hard 出处:http://www.cnblogs.com/zhili/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: