c#中类的序列化:一般化解决方法及最后的疑问

前一段时间因为频繁使用类的序列化,所以一直在思考如何能更方便的使用这个功能,现在把一段时间的学习过程和大家分享,同时还有个疑问向大家请教,如果您已经非常熟悉类的序列化,那么请您直接看第三部分。

什么是类的序列化?说白了,就是把一个类的实例转化成一段XML格式或二进制格式的数据,以便于网络传输、保存等操作。
同理,反序列化就是把XML或者二进制描述的对象还原成一个类的实例。

零、开始序列化
在C#中,要实现类的序列化并不难,以XML序列化为例,首先我们声明一个类:
[Serializable]
public class myClass
{
 ......
}
其中类声明上面的一句[Serializable]用来指示此类是可以序列化的,然后
引用两个namespace:

using System.IO;
using System.Xml.Serialization;

于是就可以执行下面代码:

 

1 Code
2 myClass cls  =   new  myClass();
3 XmlSerializer xmlSerializer  =   new  XmlSerializer(cls.GetType());
4 MemoryStream stream  =   new  MemoryStream();
5 xmlSerializer.Serialize(stream, cls);
6 byte [] buf  =  stream.ToArray();
7 string  xml  =  Encoding.ASCII.GetString(buf);
8 stream.Close();


自此,序列化就完成了,XML序列保存在string xml;变量中。
上述代码不难理解,XmlSerializer类用来提供XML序列化的功能,xmlSerializer.Serialize(stream, cls)方法,可以把类cls序列化,并将XML序列存储在流stream中。
以上便是序列化的基本方法,可以满足我们的需求。但问题是如果我们经常需要对数个类进行序列化和反序列化,就要频繁的重复上述代码,能不能让类具备自己进行序列化的方法呢?

一、第一次尝试
我首先想到,可以写一个基类,提供进行序列化的方法,任何想实现序列化的类,只需要继承此类就可以具备此方法。于是构造抽象类:

 

 

 1  public   abstract   class  SerializableBaseClass 
 2 
 3           public   virtual   string  XMLSerialize() 
 4          { 
 5              XmlSerializer xmlSerializer  =   new  XmlSerializer(GetType());  // 差异1 
 6              MemoryStream stream  =   new  MemoryStream(); 
 7              xmlSerializer.Serialize(stream,  this );  // 差异2 
 8               byte [] buf  =  stream.ToArray(); 
 9               string  xml  =  Encoding.ASCII.GetString(buf); 
10              stream.Close(); 
11               return  xml; 
12          } 
13 


上面一段代码和之前的代码只有两行不一样:
差异1:
cls.GetType()变成GetType():其中GetType()是获取当前实例的类型,在基类中调用GetType()得到的是当前实例的类型,而不是积累的类型。也就是说上述基类中调用GetType()不会得到” SerializableBaseClass”,更不会是”System.Object”,而是当前对象实例的类型。

差异2:
cls变成this:同理,this引用是指向当前实例的,只不过在基类中,不能使用this直接访问子类成员。当然通过类型转换可以达到此目的,但不在本文讨论范围内。所以Serialize(stream,this)会将整个对象序列化,而不会造成对象分割,只把基类给序列化了。

二、第二次尝试
到此为止,任何类只要集成了SerializableBaseClass就拥有了自我序列化的方法,但如何反序列化呢?我们在SerializableBaseClass类中再添加一个方法。

 

1  public   object  DeSerialize( string  xmlString)
2  {
3      XmlSerializer xmlSerializer  =   new  XmlSerializer(GetType());
4       byte [] buf  =  Encoding.ASCII.GetBytes(xmlString);
5      MemoryStream stream  =   new  MemoryStream(buf);
6       object  o  =  xmlSerializer.Deserialize(stream);
7       return  o;
8  }


此方法就实现了类的反序列化,我们可以这样使用:
声明:
[Serializable]
public class myClass : SerializableBaseClass
{
 ……
}
使用:

 

1  myClass cls  =   new  myClass();
2  string  xml  =  cls. Serialize();
3  myClass cls1  =  (myClass)cls. DeSerialize(xml);

 

这个使用乍一看没什么问题,但实际使用起来就很蹩脚
1、要想反序列化一个类,先要创建这个类的实例,只为了调用DeSerialize()
2、调用DeSerialize()返回的是object类型,需要进行类型转换

对于这两个问题,我也考虑过很久,如果把DeSerialize()定义为static,那么就不能使用GetType()方法,而其,也无法获取子类的类型。

三、最后的尝试
对于上述两个问题,想了很久,始终没想到解决办法,直到一天和朋友讨论c++STL的某个问题的时候,终于茅塞顿开,c#也是支持模板的呀,于是激动不已,改写SerializableBaseClass类:

 

 1  [Serializable()] 
 2  public   abstract   class  SerializableBaseClass  < T >  
 3 
 4       public   virtual   string  XMLSerialize() 
 5      { 
 6          XmlSerializer xmlSerializer  =   new  XmlSerializer(GetType()); 
 7          MemoryStream stream  =   new  MemoryStream(); 
 8          xmlSerializer.Serialize(stream,  this ); 
 9           byte [] buf  =  stream.ToArray(); 
10           string  xml  =  Encoding.ASCII.GetString(buf); 
11          stream.Close(); 
12           return  xml; 
13 
14 
15  public   static  T DeSerialize( string  xmlString) 
16      { 
17          XmlSerializer xmlSerializer  =   new  XmlSerializer( typeof (T)); 
18           byte [] buf  =  Encoding.ASCII.GetBytes(xmlString); 
19          MemoryStream stream  =   new  MemoryStream(buf); 
20          T o  =  (T)xmlSerializer.Deserialize(stream); 
21           return  o; 
22      } 
23 
24 

 

这样,问题就不完美的解决了(确实不完美)。为什么不完美呢?那就看下面的代码:
[Serializable]
public class myClass : SerializableBaseClass<myClass>
{
 ……
}
使用:
myClass cls = myClass.DeSerialize(xmlData);
看似上述问题都解决了,但我还是心里不爽,因为每次定义一个继承SerializableBaseClass的类,还必须把自己的类名再写一遍,放在模板类型的参数里。有没有一种方案,可以让上述myClass类的声明默认模板参数就是自身myClass,而无须再写一遍<myClass>呢?或者还有更好的其他方法?欢迎讨论。

上述的所有代码,包括二进制序列化的方法,我已经做成了c#库,欢迎大家使用、指正,互相学习。

http://www.cnblogs.com/icyplayer/archive/2009/01/08/1372097.html


XmlSerializer 泄漏:.NET Framework 中的某些部分(例如 XmlSerializer)会在内部使用动态代码生成。请看下列典型的 XmlSerializer 代码:

XmlSerializer serializer = new XmlSerializer(typeof(Person));
serializer.Serialize(outputStream, person);

XmlSerializer 构造函数将使用反射来分析 Person 类,并藉此生成一对由 XmlSerializationReader 和 XmlSerializationWriter 派生而来的类。它将创建临时的 C# 文件,将结果文件编译成临时程序集,并最终将该程序集加载到进程。通过这种方式生成的代码同样需要相当大的开销。因此 XmlSerializer 对每种类型的临时程序集进行缓存。也就是说,下一次为 Person 类创建 XmlSerializer 时,会使用缓存的程序集,而不再生成新的程序集。

默认情况下,XmlSerializer 所使用的 XmlElement 名称就是该类的名称。因此,Person 将被序列化为:

<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Id>5d49c002-089d-4445-ac4a-acb8519e62c9</Id>
 <FirstName>John</FirstName>
 <LastName>Doe</LastName>
</Person>

有时有必要在不改变类名称的前提下改变根元素的名称。(要与现有架构兼容可能需要根元素名称。)因此 Person 可能需要被序列化为 <PersonInstance>。XmlSerializer 构造函数能够很方便地被重载,将根元素名称作为第二参数,如下所示:

XmlSerializer serializer = new XmlSerializer(typeof(Person), 
  new XmlRootAttribute("PersonInstance"));

当应用程序开始对 Person 对象进行序列化/反序列化时,一切运转正常,直至引发 OutOfMemoryException。对 XmlSerializer 构造函数的重载并不会对动态生成的程序集进行缓存,而是在每次实例化新的 XmlSerializer 时生成新的临时程序集。这时应用程序以临时程序集的形式泄漏非托管内存。

要修复该泄漏,请在类中使用 XmlRootAttribute 以更改序列化类型的根元素名称:

[XmlRoot("PersonInstance")]
public class Person {
  // code
}

如果直接将属性赋予类型,则 XmlSerializer 对为类型所生成的程序集进行缓存,从而避免了内存的泄漏。如果需要对根元素名称进行动态切换,应用程序能够利用工厂对其进行检索,从而对 XmlSerializer 实例自身进行缓存。

XmlSerializer serializer = XmlSerializerFactory.Create(
  typeof(Person), "PersonInstance");

XmlSerializerFactory 是我创建的一个类,它可以使用 PersonInstance 根元素名称来检查 Dictionary<Tkey, Tvalue> 中是否包含有用于 Person 的 Xmlserializer。如果包含,则返回该实例。如果不包含,则创建一个新的实例,并将其存储在哈希表中返回给调用方。

from:http://hi.baidu.com/gotosky/blog/item/aa8717d52d246dc251da4ba0.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值