什么是序列化?
  .net的运行时环境用来支持用户定义类型的流化的机制。它是将对象实例的状态存储到存储媒体的过程。在序列化的过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。
序列化可以给我们带来什么样的方便之处呢?
        1.持久存储
       有时我们经常需要将对象的字段值保存到磁盘中,以便在以后检索此数据。虽然没有序列化也可以完成这项工作,但其方法往往是很繁琐而且容易出错的,并且在需要跟踪对象的层次结构时,会变得越来越麻烦。我们可以想 象一下编写包含大量对象的大型业务应用程序的情形,程序员不得不为每一个对象编写代码,以便将字段和属性保存至磁盘或从磁盘还原这些字段和属性。不过序列化就不一样了,它提供了轻松实现这个目标的快捷方法。
        公共语言运行时(CLR)管理对象在内存中的分布,.NET框架则通过使用反射提供自动的序列化机制。对象被序列化后,类的名称、程序集以及类实例的所有数据成员均被写入存储媒体中。对象通常用成员变量来存储对其他实例的引用。类序列化后,序列化引擎将跟踪所有已序列的引用对象,这样就确保了同一对象不被序列化多次。.NET框架所提供的序列化体系结构可以自动正确处理对象图表和循环引用。对对象图表的唯一要求是,由正在进行序列化的对象所引用的所有对象都必须标记为Serializable。否则序列化程序试图序列化未标记的对象时将为出现异常。Serializable属性用来明确表示该类可以被序列化。同样的,我们可以用NonSerializable属性用来明确表示类不能被序列化。
这样反序列化已序列化的类时,将重新创建该类,并还原所有数据成员的值。
        2.按值封送
       对象仅在创建对象的应用程序域中有效。除非对象是从MarshalByRefObject派生得到或标记为Serializable,否则,任何将对象作为参数传递或将其作为结果返回的尝试都将失败。如果对象标记为 Serializable,则该对象将被自动序列化,并从一个应用程序域传输至另一个应用程序域,然后进行反序列化,从而在第二个应用程序域中产生出该对象的一个精确副本。此过程通常称为按值封送。
序列化的实现
        序列化的机制是将类的值转化为一般的字节流,然后把这个流存储到存储媒介上。.NET框架里提供了这样两个类 BinaryFormatter和SoapFormatter。BinaryFormatter使用二进制格式化程序进行序列化。它将类中的所有成员变量(甚至标记为 private 的变量)都序列化。而SoapFormatter使用XML格式化,因此会有更好的可移植性。
下面是BinaryFormatter序列化和反序列化
 

基本序列化
        要使一个类可序列化,最简单的方法是使用 Serializable 属性对它进行标记,如下所示:

[Serializable]
public class ObjectSerializable
{
    
public int s1 = 0;
    
public int s2 = 0;
    
public String str = null;
}

        以下的方法说明了如何将此类的一个实例序列化为一个文件?
    private void Serializable()
    
{
        ObjectSerializable MyObject 
= new ObjectSerializable();
        MyObject.s1 
= 1;
        MyObject.s2 
= 24;
        MyObject.str 
= "一些字符串";
        IFormatter Formatter 
= new BinaryFormatter();
        Stream stream 
= new FileStream(Server.MapPath("MyFile.bin"),FileMode.Create,FileAccess.Write,FileShare.None);
        Formatter.Serialize(stream,MyObject);
        stream.Close();

    }

        上面的方法创建一个要使用的流和格式化程序的实例,然后调用格式化程序的 Serialize 方法。流和要序列化的对象实例作为参数提供给此调用。类中的所有成员变量(甚至标记为 private 的变量)都将被序列化,但这一点在本例中未明确体现出来。在这一点上,二进制序列化不同于只序列化公共字段的 XML 序列化程序。

        将对象还原到它以前的状态也非常容易。首先,创建格式化程序和流以进行读取,然后让格式化程序对对象进行反序列化。以下代码片段说明了如何进行此操作。
   private void ReadSerializable()
    
{
        IFormatter Formatter 
= new BinaryFormatter();
        Stream stream 
= new FileStream(Server.MapPath("MyFile.bin"),FileMode.Open,FileAccess.Read,FileShare.Read);
        ObjectSerializable MyObject 
= (ObjectSerializable)Formatter.Deserialize(stream);
        stream.Close();
        Console.WriteLine(
"s1: {0}", MyObject.s1);
        Console.WriteLine(
"s2: {0}", MyObject.s2);
        Console.WriteLine(
"str: {0}", MyObject.str);
    }

        BinaryFormatter 效率很高,能生成非常紧凑的字节流。所有使用此格式化程序序列化的对象也可使用它进行反序列化,对于序列化将在 .NET 平台上进行反序列化的对象。
        如果你想具有可移植性,请使用 SoapFormatter。所要做的更改只是将以上代码中的格式化程序换成SoapFormatter,而 Serialize 和 Deserialize 调用不变。对于上面使用的示例,该格式化程序将生成以下结果。
 <SOAP-ENV:Envelope 
            
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xmlns:xsd
="http://www.w3.org/2001/XMLSchema" 
            xmlns:SOAP-ENC
="http://schemas.xmlsoap.org/soap/encoding/" 
            xmlns:SOAP-ENV
="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:clr
="http://schemas.microsoft.com/soap/encoding/clr/1.0" 
            SOAP-ENV:encodingStyle
="http://schemas.xmlsoap.org/soap/encoding/">
-     
<SOAP-ENV:Body>
-         
<a1:ObjectSerializable id="ref-1" 
                xmlns:a1
="http://schemas.microsoft.com/clr/assem/App_Web_9qmp6o-m%2C%20Version%3D0.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
              
<s1>1</n1> 
              
<s2>24</n2> 
              
<str id="ref-3">一些字符串</str> 
          
</a1:ObjectSerializable>
      
</SOAP-ENV:Body>
  
</SOAP-ENV:Envelope>

需要注意的是,Serializable 属性是无法继承的。如果从 MyObject 派生出一个新的类,则这个新的类也必须使用该属性进行标记,否则将无法序列化。例如,如果试图序列化以下类实例,将会显示一个 SerializationException,说明 MyStuff 类型未标记为可序列化。
public class MyStuff : ObjectSerializable
{
    
public int str1 = null;
}

posted on 2007-09-24 16:06  小成  阅读(1571)  评论(0编辑  收藏  举报