C#中Serializable序列化实例详解
本文实例讲述了c#中serializable序列化。分享给大家供大家参考。具体分析如下:
概述:
序列化就是是将对象转换为容易传输的格式的过程,一般情况下转化打流文件,放入内存或者io文件 中。例如,可以序列化一个对象,然后使用 http 通过 internet 在客户端和服务器之间传输该对象,或者和其它应用程序共享使用。反之,反序列化根据流重新构造对象。
一、几种序列化技术
1)二进制序列化保持类型保真度,这对于在应用程序的不同调用之间保留对象的状态很有用。例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等等。远程处理使用序列化“通过值”在计算机或应用程序域之间传递对象。
2)xml 序列化仅序列化公共属性和字段,且不保持类型保真度。当您要提供或使用数据而不限制使用该数据的应用程序时,这一点是很有用的。由于 xml 是一个开放式标准,因此,对于通过 web 共享数据而言,这是一个很好的选择。soap 同样是一个开放式标准,这使它也成为一个颇具吸引力的选择。
3)使用提供的数据协定,将类型实例序列化和反序列化为 xml 流或文档(或者json格式)。常应用于wcf通信。
二、序列化分类
1、基本序列化
要使一个类可序列化,最简单的方法是使用 serializable 属性对它进行标记,如下所示
public class myobject
{
public int n1 = 0;
public int n2 = 0;
public string str = null;
}
将上面的类的一个实例序列化为一个文件
obj.n1 = 1;
obj.n2 = 24;
obj.str = "一些字符串";
iformatter formatter = new binaryformatter();
stream stream = new filestream("myfile.bin", filemode.create,
fileaccess.write, fileshare.none);
formatter.serialize(stream, obj);
stream.close();
上面实例的反序列化
stream stream = new filestream("myfile。bin", filemode.open,
fileaccess.read, fileshare.read);
myobject obj = (myobject) formatter.deserialize(fromstream);
stream.close();
如果要求具有可移植性,请使用 soapformatter。所要做的更改只是将以上代码中的格式化程序换成 soapformatter,而 serialize 和 deserialize 调用不变。
需要注意的是,无法继承 serializable 属性。如果从 myobject 派生出一个新的类,则这个新的类也必须使用该属性进行标记,否则将无法序列化。例如,如果试图序列化以下类实例,将会显示一个 serializationexception,说明 mystuff 类型未标记为可序列化。
2、选择性序列化
类通常包含不应被序列化的字段。例如,假设某个类用一个成员变量来存储线程 id。当此类被反序列化时,序列化此类时所存储的 id 对应的线程可能不再运行,所以对这个值进行序列化没有意义。可以通过使用 nonserialized 属性标记成员变量来防止它们被序列化,如下所示:
public class myobject
{
public int n1;
[nonserialized]
public int n2;
public string str;
}
3、自定义序列化
可以通过在对象上实现 iserializable 接口来自定义序列化过程。这一功能在反序列化后成员变量的值失效时尤其有用,但是需要为变量提供值以重建对象的完整状态。要实现 iserializable,需要实现 getobjectdata 方法以及一个特殊的构造函数,在反序列化对象时要用到此构造函数。以下代码示例说明了如何在前一部分中提到的 myobject 类上实现 iserializable。
public class myobject : iserializable
{
public int n1;
public int n2;
public string str;
public myobject()
{
}
protected myobject(serializationinfo info, streamingcontext context)
{
n1 = info.getint32("i");
n2 = info.getint32("j");
str = info.getstring("k");
}
public virtual void getobjectdata(serializationinfo info,
streamingcontext context)
{
info.addvalue("i", n1);
info.addvalue("j", n2);
info.addvalue("k", str);
}
}
在序列化过程中调用 getobjectdata 时,需要填充方法调用中提供的 serializationinfo 对象。只需按名称/值对的形式添加将要序列化的变量。其名称可以是任何文本。只要已序列化的数据足以在反序列化过程中还原对象,便可以*选择添加至 serializationinfo 的成员变量。如果基对象实现了 iserializable,则派生类应调用其基对象的 getobjectdata 方法。
需要强调的是,将 iserializable 添加至某个类时,需要同时实现 getobjectdata 以及特殊的构造函数。如果缺少 getobjectdata,编译器将发出警告。但是,由于无法强制实现构造函数,所以,缺少构造函数时不会发出警告。如果在没有构造函数的情况下尝试反序列化某个类,将会出现异常。在消除潜在安全性和版本控制问题等方面,当前设计优于 setobjectdata 方法。例如,如果将 setobjectdata 方法定义为某个接口的一部分,则此方法必须是公共方法,这使得用户不得不编写代码来防止多次调用 setobjectdata 方法。可以想象,如果某个对象正在执行某些操作,而某个恶意应用程序却调用此对象的 setobjectdata 方法,将会引起一些潜在的麻烦。
在反序列化过程中,使用出于此目的而提供的构造函数将 serializationinfo 传递给类。对象反序列化时,对构造函数的任何可见性约束都将被忽略,因此,可以将类标记为 public、protected、internal或 private。一个不错的办法是,在类未封装的情况下,将构造函数标记为 protect。如果类已封装,则应标记为 private。要还原对象的状态,只需使用序列化时采用的名称,从 serializationinfo 中检索变量的值。如果基类实现了 iserializable,则应调用基类的构造函数,以使基础对象可以还原其变量。
如果从实现了 iserializable 的类派生出一个新的类,则只要新的类中含有任何需要序列化的变量,就必须同时实现构造函数以及 getobjectdata 方法。以下代码片段显示了如何使用上文所示的 myobject 类来完成此操作。
public class objecttwo : myobject
{
public int num;
public objecttwo() : base()
{
}
protected objecttwo(serializationinfo si, streamingcontext context) :
base(si,context)
{
num = si.getint32("num");
}
public override void getobjectdata(serializationinfo si,
streamingcontext context)
{
base.getobjectdata(si,context);
si.addvalue("num", num);
}
}
切记要在反序列化构造函数中调用基类,否则,将永远不会调用基类上的构造函数,并且在反序列化后也无法构建完整的对象。 在反序列化过程中检索关键字/值对非常容易,但是,由于无法保证从散列表派生出的类已反序列化,所以把这些对象添加回散列表时会出现一些问题。因此,建议目前不要在散列表上调用方法。
三、如果对象的状态需要在不同版本间发生改变的方法
1、实现 iserializable。这使您可以精确地控制序列化和反序列化过程,在反序列化过程中正确地添加和解释未来状态。
2、使用 nonserialized 属性标记不重要的成员变量。仅当预计类在不同版本间的变化较小时,才可使用这个选项。例如,把一个新变量添加至类的较高版本后,可以将该变量标记为 nonserialized,以确保该类与早期版本保持兼容。
希望本文所述对大家的c#程序设计有所帮助。