博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅析.NET中的Serialization
阅读量:4030 次
发布时间:2019-05-24

本文共 6009 字,大约阅读时间需要 20 分钟。

 from http://arch.pconline.com.cn/pcedu/empolder/gj/vc/10305/173628.html
 摘要 本文简要介绍了.NET中的序列化(Serialization)概念,以及在代码中实作Serialization的方法。文章的最后介绍了Serialization在Clone方法中的运用。
<script src="http://www.pconline.com.cn/script/ad_text_pcedu.js" id="ad_text_pcedu" defer="defer"></script>
  
Serialization的概念
  Serialization是.NET中一种实现对象持久性(Persistent)的机制。它是一个将对象中的数据转换成一个单一元素(通常是 Stream)的过程。它的逆过程是Deserialization。Serialization的核心概念是将一个对象的所有数据看作一个独立的单元。
   一般说来,在两种情况下非常需要Serialization:1)当我们希望能够将对象当前的状态完整地保存到存储介质中,以便我们以后能够精确地还原 对象时;2)当我们希望将对象从一个应用程序空间(Application domain)传递到另一个应用程序空间时。例如,Windows Form程序就是利用Serialization机制来实现剪贴板的copy & paste的。
  .NET Framework支持两种类型的Serialization:Shallow Serialization和Deep Serialization。
  所谓Shallow Serialization是将对象的可读写(read-write)属性的值转换成字节流,而对象内部的数据(没有通过read-write属性暴露出 来的数据)则不被转换。XmlSerializer以及Web Services就使用这种技术。
  Deep Serialization比Shallow Serialization更加彻底,因为它是将存储在对象私有变量里的实际值拷贝到字节流里。而且Deep Serialization还将serialize整个object graph。也就是说,如果你的对象持有其他对象的引用,或者其他对象引用的集合,那么所有这些对象都将被Serialize。 BinaryFormatter和SoapFormatter以及.NET Remoting都使用Deep Serialization技术,它甚至被有限地用于LosFormatter来产生存储在Web Form页中的状态数据。
  本文将着重于Deep Serialization。
  
Serialization的过程
  .NET Framework通过Reflection提供自动Serialization的机制。当一个对象被序列化(Serialized)的时候,它的类 名,Assembly,以及类实例的所有数据成员都将被写入存储介质中。Serialization引擎保持对所有已经被序列化的对象引用的追踪,以确保 相同的对象引用最多只被序列化一次。
  通常,一个Serialization过程会由formatter(例如BinaryFormatter)的Serialize方法引发。对象的Serialization过程按照以下规则进行:
  1、 检测以确保formatter是否拥有一个代理选择器(surrogate selector)。如果有,检查代理选择器是否持有给定的对象类型。如果有,ISerializable.GetObjectData被调用。
  2、 如果formatter没有代理选择器,或者代理选择器没有对象类型,检查对象是否被用Serializable属性标记。如果没有,则抛出SerializationException异常。
  3、 如果对象被标记为Serializable,检查对象是否实现了ISerializable接口。如果实现了此接口,则GetObjectData被调用。
  4、 如果对象没有实现ISerializable接口,则使用默认的序列化策略,来序列化没有用NonSerialized属性标记的域。
使你的class能够被序列化
  通过上面对Serialization过程的分析,我们可以看出,有两种方式可以使一个class能够被序列化:1)将此class简单地标记为 Serializable;2)为此class实现ISerializable接口,并将此class标记为Serializable。
  1、 标记Serializable属性
  标记Serializable属性的方式是实现Serialization的基本方法。举个简单的例子:
  [Serializable]
  public class Person
  {
  public string name = null;
  public int age = 0;
  }
  你可以使用BinaryFormatter来将上面的class序列化:
  Person sam = new Person();
  sam.name = "sam";
  sam.age = 24;
  IFormatter formatter = new BinaryFormatter();
  Stream stream = new FileStream("sam.dat",
  FileMode.Create, FileAccess.Write, FileShare.None);
  formatter.Serialize(stream, sam);
  stream.Close();
  就是这么简单,你所要做的就是创建一个Stream和一个formatter的实例,然后调用formatter的Serialize方法。经过 BinaryFormatter serialize的数据仍然能够通过BinaryFormatter deserialize回来,方法与serialize同样简单,这里就不赘述了。
  如果你不想将类里的所有域都序列化,可以使用NonSerialized属性进行选择。如:
  [Serializable]
  public class Person
  {
  public string name = null;
  [NonSerialized]
  public int age = 0;
  }
  这样,age域就不会被序列化了。
  需要注意的是,Serializable属性并不能被继承。也就是说如果你希望Person的派生类也能够被Serialize的话,那么这个派生类也必须被Serializable标记。否则将得到SerializationException异常。
  同样的,Person类中的所有对其他类的引用,其所引用的类都应该是能够被Serialize的。.NET Framework中的大部分class都实现了ISerializable接口,但有些class没有实现,例如ImageList。可以通过MSDN Library的到一个实现了ISerializable接口的class列表。对那些没有实现此接口的class,使用的时候要当心。
2、 实现ISerializable接口
  Serializable属性的功能非常强大,它使得Serialize和Deserialize变得十分简单。但凡事有利必有弊,由 Serializable实现的自动序列化方法有时不够灵活。我们并不能完全控制Serialize和Deserialize的行为,而有些时候它们的行 为对我们来说很重要。那么我们通过何种方法能够控制Serialize和Deserialize的行为呢?答案就是,自己来实现 ISerializable接口。ISerializable接口给予我们更大的自由来控制Serialize和Deserialize,但是无疑我们将 不得不写更多的代码L。
  下面我们来看看如何实现ISerializabe接口。ISerializable接口位于System.Runtime.Serialization名字空间中,声明如下:
  public inferface ISerializable
  {
  void GetObjectData(SerializationInfo info,
  StreamingContext context);
  }
  它只有一个方法GetObjectData。因此,像实现其他接口一样,我们必须实现此方法。但与其他接口不同的是,为了 Deserialization,我们还必须实现一个特殊的构造函数(我称此构造函数为“序列化构造函数”),此构造函数具有与 GetObjectData相同的参数列表。由于此构造函数专门用于.NET Framework在Deserialize时的Reflection机制,因此我们通常将它声明为保护或私有模式。如下:(当然,如果你的class只 需要Serialize而不需要Deserialize的话,也可以不实现这个特殊的构造函数)
  [Serializable]
  public class Person : ISerializable
  {
  public string name = null;
  public int age = 0;
  public Person()
  {
  }
  protected Person(SerializationInfo info, StreamingContext context)
  {
  name = info.GetString("name");
  age = info.GetInt32("age");
  }
  void ISerializable.GetObjectData(SerializationInfo info,
  StreamingContext context)
  {
  info.AddValue("name", name);
  info.AddValue("age", age);
  }
  }
  通过实现ISerializable接口,使得我们有机会在ISerializable.GetObjectData中控制Serialize的行 为,在“序列化构造函数”中控制Deserialize的行为。这个接口提供给我们的信息非常全面而灵活,以致于我们甚至可以在这两个方法中耍些花招。比 如,我们可以在Deserialize的时候,籍由改变info.FullTypeName来得到一种与被Serialize的对象不同类型的另一个对象 等。
独辟蹊径
  前面谈到过Serialization被运用的典型环境,是对象存储、进程间数据传递等涉及到对象持久性的领域。但实际上,它也能够被运用到其他的许 多地方,关键在于我们是否能想到去用运Serialization,有时候思维定式也是很可怕的J。举个例子,我们来看看在Clone方法中如何使用 Serialization[1]。
  如果我们要为Person类实现Clone方法,我们通常会这样写:
  [Serializable]
  public class Person : ICloneable
  {
  public string name = null;
  public int age = 0;
  public object Clone()
  {
  Person person = new Person();
  person.name = name;
  person.age = age;
  return person;
  }
  }
  如果我们利用Serialization的方法,Clone函数就能写成下面的样子:
  public object Clone()
  {
  MemoryStream stream = new MemoryStream();
  BinaryFormatter formatter = new BinaryFormatter();
  formatter.Serialize(stream, this);
  stream.Position = 0;
  return formatter.Deserialize(stream);
  }
  从这两个实现上看,使用Serialization实现Clone方法似乎并没有什么好处。可是设想如果你面对的是一个复杂的类继承体系,从基类到派 生类都需要实现Clone方法。利用第一种实作手法,你将不得不为每一个class写一个Clone方法,而且随着数据成员的增多,这个方法将越来越冗 长,并且会由于数据成员的改变而引发错误(我曾经遇到过好几次,由于class中增加了成员变量,而Clone方法没有及时更新,导致运行时错误。呵呵, 这种错误还很难调试)。现在你看到用Serialization实现的好处了吧?是的,我们只要在基类中将Clone方法声明为virtual,并用 Serialization的方法实现之,然后保证基类和派生类都可以被Serialize,上面所有的麻烦不都迎刃而解了吗?
  
总结
  现代软件项目中,无论何种项目都会或多或少地涉及到对象持久性的问题,.NET也不例外,无论是Windows Form、ASP.NET,还是Web Services,都需要处理对象持久性。而Serialization正是.NET为应对这个问题而给出的解法。
  参考文献
  ·[1] Rockford Lhotka,《Object Serialization in Visual Basic .NET》,MSDN Library。Serialization在Clone方法中的运用即来自此文。
  ·Piet Obermeyer and Jonathan Hawkins,《Object Serialization in the .NET Framework》,MSDN Library。
  ·Jeffrey Richter,《.NET Run-time Serialization》Part 1,Part 2,Part 3,MSDN Library。

转载地址:http://bjqbi.baihongyu.com/

你可能感兴趣的文章
mysql:sql alter table 修改列属性的字符集
查看>>
mysql:sql drop table (删除表)
查看>>
mysql:sql truncate (清除表数据)
查看>>
scrapy:xpath string(.)非常注意问题
查看>>
yuv to rgb 转换失败呀。天呀。谁来帮帮我呀。
查看>>
yuv420 format
查看>>
单纯的把Y通道提取出来能正确显示出灰度图来为什么我的Qt就显示不出来呢转换有问题呀?
查看>>
YUV420只绘制Y通道
查看>>
yuv420 还原为RGB图像
查看>>
LED恒流驱动芯片
查看>>
驱动TFT要SDRAM做为显示缓存
查看>>
使用file查看可执行文件的平台性,x86 or arm ?
查看>>
qt5 everywhere 编译summary
查看>>
qt5 everywhere编译完成后,找不到qmake
查看>>
arm-linux开机读取硬件时钟,设置系统时钟。
查看>>
交叉编译在x86上调试好的qt程序
查看>>
/dev/input/event0 键盘输入
查看>>
qt 创建异形窗体
查看>>
可重入函数与不可重入函数
查看>>
简单Linux C线程池
查看>>