zoukankan      html  css  js  c++  java
  • Serializable 指示一个类可以序列化;ICloneable支持克隆,即用与现有实例相同的值创建类的新实例(接口);ISerializable允许对象控制其自己的序列化和反序列化过程(接口)

    Serializable

    序列化是指将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本

    在 面向对象的环境中实现序列化机制时,必须在易用性和灵活性之间进行一些权衡。只要您对此过程有足够的控制能力,就可以使该过程在很大程度上自动进行。例 如,简单的二进制序列化不能满足需要,或者,由于特定原因需要确定类中那些字段需要序列化。

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

    **************************************************************************************
    
    [Serializable]
    public Class MyObject {
       public int n1 = 0;
       public int n2 = 0;
       public String str = null;
    
    }
    **************************************************************************************
    Serializable

    以下代码片段说明了如何将此类的一个实例序列化为一个文件:

    1 MyObject obj = new MyObject();
    2 obj.n1 = 1;
    3 obj.n2 = 24;
    4 obj.str = "一些字符串";
    5 IFormatter formatter = new BinaryFormatter();
    6 Stream stream = new FileStream("MyFile.bin", FileMode.Create,FileAccess.Write, FileShare.None);
    7 formatter.Serialize(stream, obj);
    8 stream.Close();
    实例序列化为一个文件

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

    将对象还原到它以前的状态也非常容易。首先,创建格式化程序和流以进行读取,然后让格式化程序对对象进行反序列化。以下代码片段说明了如何进行此操作。

    1 IFormatter formatter = new BinaryFormatter();
    2 Stream stream = new FileStream("MyFile.bin", FileMode.Open,FileAccess.Read, FileShare.Read);
    3 MyObject obj = (MyObject) formatter.Deserialize(fromStream);
    4 stream.Close();
    5 
    6 // 下面是输出验证
    7 Console.WriteLine("n1: {0}", obj.n1);
    8 Console.WriteLine("n2: {0}", obj.n2); Console.WriteLine("str: {0}", obj.str);
    反序列化

    自定义序列化 

    可以通过在对象上实现 ISerializable 接口来自定义序列化过程。这一功能在反序列化后成员变量的值失效时尤其有用,但是需要为变量提供值以重建对象的完整状态。要实现 ISerializable,需要实现 GetObjectData 方法以及一个特殊的构造函数,在反序列化对象时要用到此构造函数。以下代码示例说明了如何在前一部分中提到的 MyObject 类上实现 ISerializable。

     1 [Serializable]
     2 public class MyObject : ISerializable
     3 {
     4    public int n1;
     5    public int n2;
     6    public String str;
     7 
     8    public MyObject()
     9    {
    10    }
    11 
    12    protected MyObject(SerializationInfo info, StreamingContext context)
    13    {
    14    n1 = info.GetInt32("i");
    15    n2 = info.GetInt32("j");
    16    str = info.GetString("k");
    17    }
    18 
    19    public virtual Void GetObjectData(SerializationInfo info,
    20 StreamingContext context)
    21    {
    22    info.AddValue("i", n1);
    23    info.AddValue("j", n2);
    24    info.AddValue("k", str);
    25    }
    26 }
    序列化接口

     序列化过程中调用 GetObjectData 时,需要填充方法调用中提供的 SerializationInfo 对象。只需按名称/值对的形式添加将要序列化的变量。其名称可以是任何文本。只要已序列化的数据足以在反序列化过程中还原对象,便可以自由选择添加至 SerializationInfo 的成员变量。如果基对象实现了 ISerializable,则派生类应调用其基对象的 GetObjectData 方法。

    需要强调的是,将 ISerializable 添加至某个类时,需要同时实现 GetObjectData 以及特殊的构造函数。如果缺少 GetObjectData,编译器将发出警告。但是,由于无法强制实现构造函数,所以,缺少构造函数时不会发出警告。如果在没有构造函数的情况下尝试反 序列化某个类,将会出现异常。在消除潜在安全性和版本控制问题等方面,当前设计优于 SetObjectData 方法。例如,如果将 SetObjectData 方法定义为某个接口的一部分,则此方法必须是公共方法,这使得用户不得不编写代码来防止多次调用 SetObjectData 方法。可以想象,如果某个对象正在执行某些操作,而某个恶意应用程序却调用此对象的 SetObjectData 方法,将会引起一些潜在的麻烦。

    在反序列化过程中,使用出于此目的而提供的构造函数将 SerializationInfo 传递给类。对象反序列化时,对构造函数的任何可见性约束都将被忽略,因此,可以将类标记为 public、protected、internal 或 private。一个不错的办法是,在类未封装的情况下,将构造函数标记为 protect。如果类已封装,则应标记为 private。要还原对象的状态,只需使用序列化时采用的名称,从 SerializationInfo 中检索变量的值。如果基类实现了 ISerializable,则应调用基类的构造函数,以使基础对象可以还原其变量。

    如果从实现了 ISerializable 的类派生出一个新的类,则只要新的类中含有任何需要序列化的变量,就必须同时实现构造函数以及 GetObjectData 方法。以下代码片段显示了如何使用上文所示的 MyObject 类来完成此操作。

    切记要在反序列化构造函数中调用基类,否则,将永远不会调用基类上的构造函数,并且在反序列化后也无法构建完整的对象。

    对 象被彻底重新构建,但是在反系列化过程中调用方法可能会带来不良的副作用,因为被调用的方法可能引用了在调用时尚未反序列化的对象引用。如果正在进行反序 列化的类实现了 IDeserializationCallback,则反序列化整个对象图表后,将自动调用 OnSerialization 方法。此时,引用的所有子对象均已完全还原。有些类不使用上述事件侦听器,很难对它们进行反序列化,散列表便是一个典型的例子。在反序列化过程中检索关键 字/值对非常容易,但是,由于无法保证从散列表派生出的类已反序列化,所以把这些对象添加回散列表时会出现一些问题。因此,建议目前不要在散列表上调用方 法。

    序列化过程的步骤 
    在格式化程序上调用 Serialize 方法时,对象序列化按照以下规则进行:

    检查格式化程序是否有代理选取器。如果有,检查代理选取器是否处理指定类型的对象。如果选取器处理此对象类型,将在代理选取器上调用 ISerializable.GetObjectData。
    如果没有代理选取器或有却不处理此类型,将检查是否使用 Serializable 属性对对象进行标记。如果未标记,将会引发 SerializationException。
    如果对象已被正确标记,将检查对象是否实现了 ISerializable。如果已实现,将在对象上调用 GetObjectData。
    如果对象未实现 Serializable,将使用默认的序列化策略,对所有未标记为 NonSerialized 的字段都进行序列化。

    版本控制 
    .NET 框架支持版本控制和并排执行,并且,如果类的接口保持一致,所有类均可跨版本工作。由于序列化涉及的是成员变量而非接口,所以,在向要跨版本序列化的类中 添加成员变量,或从中删除变量时,应谨慎行事。特别是对于未实现 ISerializable 的类更应如此。若当前版本的状态发生了任何变化(例如添加成员变量、更改变量类型或更改变量名称),都意味着如果同一类型的现有对象是使用早期版本进行序 列化的,则无法成功对它们进行反序列化。

    如果对象的状态需要在不同版本间发生改变,类的作者可以有两种选择:

    实现 ISerializable。这使您可以精确地控制序列化和反序列化过程,在反序列化过程中正确地添加和解释未来状态。
    使用 NonSerialized 属性标记不重要的成员变量。仅当预计类在不同版本间的变化较小时,才可使用这个选项。例如,把一个新变量添加至类的较高版本后,可以将该变量标记为 NonSerialized,以确保该类与早期版本保持兼容。

    序列化规则 
    由于类编译后便无法序列化,所以在设计新类时应考虑序列化。需要考虑的问题有:是否必须跨应用程序域来发送此类?是否要远程使用此类?用户将如何使用此类? 也许他们会从我的类中派生出一个需要序列化的新类。只要有这种可能性,就应将类标记为可序列化。除下列情况以外,最好将所有类都标记为可序列化:

    所有的类都永远也不会跨越应用程序域。如果某个类不要求序列化但需要跨越应用程序域,请从 MarshalByRefObject 派生此类。
    类存储仅适用于其当前实例的特殊指针。例如,如果某个类包含非受控的内存或文件句柄,请确保将这些字段标记为 NonSerialized 或根本不序列化此类。
    某些数据成员包含敏感信息。在这种情况下,建议实现 ISerializable 并仅序列化所要求的字段。

     1 [Serializable]
     2 public class ObjectTwo : MyObject
     3 {
     4    public int num;
     5 
     6    public ObjectTwo() : base()
     7    {
     8    }
     9 
    10    protected ObjectTwo(SerializationInfo si, StreamingContext context) :base(si,context)
    11    {
    12    num = si.GetInt32("num");
    13    }
    14 
    15    public override void GetObjectData(SerializationInfo si,
    16 StreamingContext context)
    17    {
    18    base.GetObjectData(si,context);
    19    si.AddValue("num", num);
    20    }
    21 }
    View Code
  • 相关阅读:
    [原创]Linux下压力测试工具Webbench介绍
    [原创] 测试策略是什么?
    [原创]测试报告模板
    [原创]性能测试基础知识
    [原创]性能测试工具介绍
    [原创]LoadRunner性能测试过程
    [内部资料]LoadRunner培训初级教程
    [原创]什么是性能测试?
    [原创]软件测试CheckList文档
    开源博客秋色园QBlog多用户博客系统安装视频教程
  • 原文地址:https://www.cnblogs.com/12xiaole/p/8319839.html
Copyright © 2011-2022 走看看