zoukankan      html  css  js  c++  java
  • c#中类的序列化:一般化解决方法及最后的疑问

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

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

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

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

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

    1Code
    2myClass cls = new myClass();
    3XmlSerializer xmlSerializer = new XmlSerializer(cls.GetType());
    4MemoryStream stream = new MemoryStream();
    5xmlSerializer.Serialize(stream, cls);
    6byte[] buf = stream.ToArray();
    7string xml = Encoding.ASCII.GetString(buf);
    8stream.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>呢?或者还有更好的其他方法?欢迎讨论。

  • 相关阅读:
    存储过程生成POCO
    Asp.net MVC4与Razor呈现图片的扩展
    Html5中新input标签与Asp.net MVC应用程序
    HTML5上传文件显示进度
    JQuery图表插件之Flot
    用Html5与Asp.net MVC上传多个文件
    TSQL列出最后访问的存储过程
    Asp.net MVC 限制一个方法到指定的Submit按钮
    VisualStudio2012轻松把JSON数据转换到POCO的代码
    Apache Tika源码研究(三)
  • 原文地址:https://www.cnblogs.com/IsNull/p/1372129.html
Copyright © 2011-2022 走看看