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 }

原文地址:https://www.cnblogs.com/12xiaole/p/8319839.html

时间: 2024-10-07 20:32:25

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

设计模式之创建类模式PK

创建类模式包括: 工厂方法模式 建造者模式 抽象工厂模式 单例模式 原型模式 创建类模式能够提供对象的创建和管理职责. 其中单例模式和原型模式非常容易理解, 单例模式是要保持在内存中只有一个对象,原型模式是要求通过复制的方式产生一个新的对象,这两个不容易混淆. 工厂方法模式VS建造者模式 工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程,旨在通过一步一步的精确构造创建出一个复杂的对象. 工厂方法模式和建造者模式的区别: 意图不同. 在工厂方法模式中, 我们关注的是产品的

编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程

建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttribute.OnDeserializingAttribute.OnSerializedAttribute.OnSerializingAttribute.NoSerializable等特性不能完全满足自定义序列化的要求,那就需要继承ISerializable了. 以下是格式化器的工作流程:如果格式化器

Java对象表示方式1:序列化、反序列化和transient关键字的作用

http://www.cnblogs.com/xrq730/p/4821958.html 平时我们在Java内存中的对象,是无 法进行IO操作或者网络通信的,因为在进行IO操作或者网络通信的时候,人家根本不知道内存中的对象是个什么东西,因此必须将对象以某种方式表示出来,即 存储对象中的状态.一个Java对象的表示有各种各样的方式,Java本身也提供给了用户一种表示对象的方式,那就是序列化.换句话说,序列化只是表示对 象的一种方式而已.OK,有了序列化,那么必然有反序列化,我们先看一下序列化.反序

对象和XML之间的序列化和反序列化

JAXB主要用来实现对象和XML之间的序列化和反序列化 用于序列化的XmlUtil package cn.lzrabbit; import java.io.StringReader; import java.io.StringWriter; import javax.xml.bind.*; public class XmlUtil { public static String toXML(Object obj) { try { JAXBContext context = JAXBContext.

C#操作SqlServer MySql Oracle通用帮助类Db_Helper_DG(默认支持数据库读写分离、查询结果实体映射ORM)

[前言] 作为一款成熟的面向对象高级编程语言,C#在ADO.Net的支持上已然是做的很成熟,我们可以方便地调用ADO.Net操作各类关系型数据库,在使用了多年的Sql_Helper_DG后,由于项目需要,于是乎,就准备写一个Mysql_Helper在实现过程中,发现ADO.Net封装之完善,以及面向对象的封装.继承.多态,有了这些特性,何不把数据库操作封装成为一个通用的类呢,此文由此铺展而来... [实现功能] 这篇文章将要介绍的主要内容如下: 1.ADO.NET之SqlServer 2.ADO

一个简单的Web服务器-支持Servlet请求

上接 一个简单的Web服务器-支持静态资源请求,这个服务器可以处理静态资源的请求,那么如何处理Servlet请求的呢? 判断是否是Servlet请求 首先Web服务器需要判断当前请求是否是Servlet请求. 像Tomcat,通过解析HTTP报文拿到请求url后,就可以根据web.xml来查找是否有匹配的Servlet,如果有匹配则认定为是一个有效的Servlet请求,然后将request,response传给对应的servlet的service()方法. 这里既然要实现一个简单的Web服务器,

【OC复合题】之定义一个学生类,需要有姓名,年龄,考试成绩三个成员属性,创建5个对象,属性可以任意值。(Objective-C)

题目: 定义一个学生类,需要有姓名,年龄,考试成绩三个成员属性,创建5个对象,属性可以任意值.(Objective-C) 1)    不使用@property,手动编写他们的访问器方法(getter和setter),注意内存管理(手动管理内存) 2)    增加一个便利构造器(快速构造器) 3)    使用NSLog输出学生对象时,输出信息格式为:My Name Is XXX  Age Is XXX Score Is XXX 4)    对5个学生对象按照成绩->年龄->姓名优先级排序(成绩相

python 第一个Python类(创建类)

'''设计类类名:见名之意,首字母大写,其他遵循驼峰原则属性:见名之意,其他遵循驼峰原则行为(方法/功能):见名之意,其他遵循驼峰原则 类名:Wife属性:sex age height weight faceValue行为:做饭 洗衣服 拖地 揉肩 捶腿 类名:Husband属性:sex age height weight faceValue行为:吃饭 看电视 打游戏 被揉肩 被捶腿 类名:Car属性: color type行为:跑''' '''创建类类:一种数据类型,本身并不占内存空间,根所学

使用反射和泛型,动态读取XML创建类实例并赋值

[狗刨学习网]unity极致学院,致力于打造业内unity3d培训.学习第一品牌. 前言: 最近小匹夫参与的游戏项目到了需要读取数据的阶段了,那么觉得自己业余时间也该实践下数据相关的内容.那么从哪入手呢?因为用的是Unity3d的游戏引擎,思来想去就选择了C#读取XML文件这个小功能.网上的例子倒也不少,但总是觉得缺点什么.比如读取xml文件之后该如何处理?看到的文章基本上都是手动创建一个目标类的实例,然后手动从读取的XML文件的内容中给刚才创建的目标类实例相关字段赋值.缺点什么呢?对嘞,感觉上