序列化通俗地讲就是将一个对象转换成一个字节流的过程,这样就可以轻松保存在磁盘文件或数据库中。反序列化是序列化的逆过程,就是将一个字节流转换回原来的对象的过程。

然而为什么需要序列化和反序列化这样的机制呢?这个问题也就涉及到序列化和反序列化的用途了,

对于序列化的主要用途有:

1)、将应用程序的状态保存在一个磁盘文件或数据库中,并在应用程序下次运行时恢复状态。例如, Asp.net 中利用序列化和反2)、序列化来保存和恢复会话状态。
3)、一组对象可以轻松复制到Windows 窗体的剪贴板中,再粘贴回同一个或者另一个应用程序。
将对象按值从一个应用程序域中发送到另一个程序域
并且如果把对象序列化成内存中的字节流,就可以利用一些其他的技术来处理数据,例如,对数据进行加密和压缩等。

序列化和反序列化的简单使用:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
 
namespace Serializable
{
 [Serializable]
 public class Person
 {
  public string personName;
 
  [NonSerialized]
  public string personHeight;
 
  private int personAge;
  public int PersonAge
  {
   get { return personAge; }
   set { personAge = value; }
  }
 
  public void Write()
  {
   Console.WriteLine("Person Name: "+personName);
   Console.WriteLine("Person Height: " +personHeight);
   Console.WriteLine("Person Age: "+ personAge);
  }
   
 }
 class Program
 {
  static void Main(string[] args)
  {
   Person person = new Person();
   person.personName = "Jerry";
   person.personHeight = "175CM";
   person.PersonAge = 22;
   Stream stream = Serialize(person);
 
   //为了演示,都重置
   stream.Position = 0;
   person = null;
 
   person = Deserialize(stream);
   person.Write();
   Console.Read();
    
  }
  private static MemoryStream Serialize(Person person)
  {
   MemoryStream stream = new MemoryStream();
 
   // 构造二进制序列化格式器
   BinaryFormatter binaryFormatter = new BinaryFormatter();
   // 告诉序列化器将对象序列化到一个流中
   binaryFormatter.Serialize(stream, person);
 
   return stream;
 
  }
 
  private static Person Deserialize(Stream stream)
  {
   BinaryFormatter binaryFormatter = new BinaryFormatter();
   return (Person)binaryFormatter.Deserialize(stream);
  }
   
 }
}
主要是调用System.Runtime.Serialization.Formatters.Binary命名空间下的BinnaryFormatter类来进行序列化和反序列化,

从中可以看出除了标记NonSerialized的其他成员都能序列化,注意这个属性只能应用于一个类型中的字段,而且会被派生类型继承。

SOAP 和XML 的序列化和反序列化和上面类似,只需要改下格式化器就可以了, 这里我就不列出来了。

三、控制序列化和反序列化
  有两种方式来实现控制序列化和反序列化:

通过OnSerializing, OnSerialized,OnDeserializing, OnDeserialized,NonSerialized和OptionalField等属性
实现System.Runtime.Serialization.ISerializable接口
第一种方式实现控制序列化和反序列化代码:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
 
namespace ControlSerialization
{
  [Serializable]
  public class Circle
  {
    private double radius; //半径
    [NonSerialized]
    public double area; //面积
 
    public Circle(double inputradiu)
    {
      radius = inputradiu;
      area = Math.PI * radius * radius;
    }
 
    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
      area = Math.PI * radius * radius;
    }
 
    public void Write()
    {
      Console.WriteLine("Radius is: " + radius);
      Console.WriteLine("Area is: " + area);
    }
  }
  class Program
  {
     
    static void Main(string[] args)
    {
      Circle c = new Circle(10);
      MemoryStream stream =new MemoryStream();
      BinaryFormatter formatter = new BinaryFormatter();
      // 将对象序列化到内存流中,这里可以使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
      formatter.Serialize(stream,c);
      stream.Position = 0;
      c = null;
      c = (Circle)formatter.Deserialize(stream);
      c.Write();
      Console.Read();
 
    }
  }
}

注意:如果注释掉 OnDeserialized属性的话,area字段的值就是0了,因为area字段没有被序列化到流中。

在上面需要序列化的对象中,格式化器只会序列化对象的radius字段的值。area字段中的值不会序列化,因为该字段已经应用了NonSerializedAttribute属性,然后我们用Circle c=new Circle(10)这样代码构建一个Circle对象时,在内部,area会设置一个约为314.159这样的值,这个对象序列化时,只有radius的字段的值(10)写入流中, 但当反序列化成一个Circle对象时,它的area字段的值会初始化为0,而不是约314.159的一个值。为了解决这样的问题,所以自定义一个方法应用OnDeserializedAttribute属性。此时的执行过程为:每次反序列化类型的一个实例,格式化器都会检查类型中是否定义了 一个应用了该attribute的方法,如果是,就调用该方法,调用该方法时,所有可序列化的字段都会被正确设置。除了OnDeserializedAttribute这个定制attribute,system.Runtime.Serialization命名空间还定义了OnSerializingAttribute,OnSerializedAttribute和OnDeserializingAttribute这些定制属性。

实现ISerializable接口方式控制序列化和反序列化代码:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;
 
namespace ControlSerilization2
{
  [Serializable]
  public class MyObject : ISerializable
  {
    public int n1;
    public intn2;
 
    [NonSerialized]
    public String str;
 
    public MyObject()
    {
    }
 
    protected MyObject(SerializationInfo info, StreamingContext context)
    {
      n1 = info.GetInt32("i");
      n2 = info.GetInt32("j");
      str = info.GetString("k");
    }
 
    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      info.AddValue("i", n1);
      info.AddValue("j", n2);
      info.AddValue("k", str);
    }
 
    public void Write()
    {
      Console.WriteLine("n1 is: " + n1);
      Console.WriteLine("n2 is: " + n2);
      Console.WriteLine("str is: " + str);
    }
  }
 
  class Program
  {
    static void Main(string[] args)
    {
      MyObject obj = new MyObject();
      obj.n1 = 2;
      obj.n2 = 3;
      obj.str = "Jeffy";
      MemoryStream stream = new MemoryStream();
      BinaryFormatter formatter = new BinaryFormatter();
      // 将对象序列化到内存流中,这里可以使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
      formatter.Serialize(stream, obj);
      stream.Position = 0;
      obj = null;
      obj = (MyObject)formatter.Deserialize(stream);
      obj.Write();
      Console.Read();
    }
  }
}
此时的执行过程为:当格式化器序列化对象时,会检查每个对象,如果发现一个对象的类型实现了ISerializable接口,格式化器会忽视所有定制属性,改为构造一个新的System.Runtime.Serialization.SerializationInfo对象,这个对象包含了要实际为对象序列化的值的集合。构造好并初始化好SerializationInfo对象后,格式化器调用类型的GetObjectData方法,并向它传递对SerializationInfo对象的引用,GetObjectData方法负责决定需要哪些信息来序列化对象,并将这些信息添加到SerializationInfo对象中,通过调用AddValue方法来添加需要的每个数据,添加好所有必要的序列化信息后,会返回至格式化器,然后格式化器获取已经添加到SerializationInfo对象中的所有值,并将它们都序列化到流中,当反序列化时,格式化器从流中提取一个对象时,会为新对象分配内存,最初,这个对象的所有字段都设为0或null,然后,格式化器检查类型是否实现了ISerializable接口,如果存在这个接口, 格式化器就尝试调用一个特殊构造器,它的参数和GetObjectData方法的完全一致。

四、格式化器如何序列化和反序列化
从上面的分析中可以看出,进行序列化和反序列化主要是格式化器在工作的,然而下面就是要讲讲格式化器是如何序列化一个应用了 SerializableAttribute 属性的对象。

1、格式化器调用FormatterServices的GetSerializableMembers方法:public static MemberInfo[] GetSerializableMembers(Type type,StreamingContext context);这个方法利用发射获取类型的public和private实现字段(标记了NonSerializedAttributee属性的字段除外)。方法返回由MemberInfo对象构成的一个数组,其中每个元素对应于一个可序列化的实例字段。
2、对象被序列化,System.Reflection.MemberInfo对象数组传给FormatterServices的静态方法GetObjectData: public static object[] GetObjectData(Object obj,MemberInfo[] members);  这个方法返回一个Object数组,其中每个元素都标识了被序列化的那个对象中的一个字段的值。
3、格式化器将程序集标识和类型的完整名称写入流中。
4、格式化器然后遍历两个数组中的元素,将每个成员的名称和值写入流中。
接下来是解释格式化器如何自动反序列化一个应用了 SerializableAttribute属性的对象。

1、格式化器从流中读取程序集标识和完整类型名称。
2、格式化器调用FormatterServices的静态方法GetUninitializedObject: public static Object GetUninitializedObject(Type ttype);这个方法为一个新对象分配内存,但不为对象调用构造器。然而,对象的所有字段都被初始化为0或null.
3格式化器现在构造并初始化一个MemberInfo数组,调用FormatterServices的GetSerializableMembers方法,这个方法返回序列化好、现在需要反序列化的一组字段。
4、格式化器根据流中包含的数据创建并初始化一个Object数组。
5、将对新分配的对象、MemberInfo数组以及并行Object数组的引用传给FormatterServices的静态方法PopulateObjectMembers:
          public static Object PopulateObjectMembers(Object obj,MemberInfo[] members,Object[] data);这个方法遍历数组,将每个字段初始化成对应的值。

注:格式化如何序列化和反序列对象部分摘自CLR via C#(第三版),写在这里可以让初学者进一步理解格式化器在序列化和反序列化过程中所做的工作。

写到这里这篇关于序列化和反序列的文章终于结束了, 希望对朋友有帮助。

.Net中的序列化和反序列化详解的更多相关文章

  1. Java中的序列化Serialable高级详解

    来自[http://blog.csdn.net/jiangwei0910410003/article/details/18989711] 引言 将 Java 对象序列化为二进制文件的 Java 序列化 ...

  2. go语言之行--文件操作、命令行参数、序列化与反序列化详解

    一.简介 文件操作对于我们来说也是非常常用的,在python中使用open函数来对文件进行操作,而在go语言中我们使用os.File对文件进行操作. 二.终端读写 操作终端句柄常量 os.Stdin: ...

  3. C# 序列化和反序列化 详解

    什么是序列化以及如何实现序列化? 如何将对象数据写入 XML 文件? 如何从 XML 文件读取对象数据? 什么是序列化以及如何实现序列化? 序列化是通过将对象转换为字节流,从而存储对象或将对象传输到内 ...

  4. 《Java基础知识》序列化与反序列化详解

    序列化的作用:为了不同jvm之间共享实例对象的一种解决方案.由java提供此机制. 序列化应用场景: 1. 分布式传递对象. 2. 网络传递对象. 3. tomcat关闭以后会把session对象序列 ...

  5. 前端后台以及游戏中使用Google Protocol Buffer详解

    前端后台以及游戏中使用Google Protocol Buffer详解 0.什么是protoBuf protoBuf是一种灵活高效的独立于语言平台的结构化数据表示方法,与XML相比,protoBuf更 ...

  6. PHP 中 16 个魔术方法详解

    PHP 中 16 个魔术方法详解   前言 PHP中把以两个下划线__开头的方法称为魔术方法(Magic methods),这些方法在PHP中充当了举足轻重的作用. 魔术方法包括: __constru ...

  7. php中的PDO函数库详解

    PHP中的PDO函数库详解 PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力:与ADODB和MDB2相比,P ...

  8. [ 转载 ] Java开发中的23种设计模式详解(转)

    Java开发中的23种设计模式详解(转)   设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类 ...

  9. php中流行的rpc框架详解

    什么是RPC框架? 如果用一句话概括RPC就是:远程调用框架(Remote Procedure Call) 那什么是远程调用? 我的官方群点击此处. 通常我们调用一个php中的方法,比如这样一个函数方 ...

随机推荐

  1. 【Android】6.1 Toast(信息提示框)

    分类:C#.Android.VS2015: 创建日期:2016-02-08 一.简介 Toast用于向用户显示一些帮助或者提示信息.前面我们已经多次用到它,这里只是系统地将其总结一下,并演示它的各种基 ...

  2. ueditor图片上传配置

    ueditor图片上传配置文件为ueditor/php/config.json /* 上传图片配置项 */ "imageActionName": "uploadimage ...

  3. Chrome浏览器缓存查看工具-ChromeCacheView

    最近想听一下最新的流行热歌,按着某网站的新歌排行榜逐首在巨鲸音乐网搜索下载,但相当一部分的歌曲还是没能下载到,逼不得已只能到百度MP3下载,在搜索结果中已经挑体积比较大的文件来下载了,但下载到的MP3 ...

  4. ny36 最长公共子序列

    最长公共子序列 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 咱们就不拐弯抹角了,如题,需要你做的就是写一个程序,得出最长公共子序列.tip:最长公共子序列也称作最 ...

  5. jquery,checkbox无法用attr()二次勾选

    今晨,漂亮的测试妹妹提了个奇怪的bug,说我一功能checkbox时隐时现,比如第一次打开有勾选,第n次打开可能就不选了. 想到与美女有亲密接触机会,马上鸡动起来. 经过偶层层抽次剥茧(da da j ...

  6. vue自动化单元测试

    // 引用vue和需要测试的组件 import Vue from 'vue' import HelloWorld from '@/components/HelloWorld' // 创建测试套件,一个 ...

  7. 一款基于jquery实现的鼠标单击出现水波特效

    今天要为大家绍一款由jquery实现的鼠标单击出现水波特效.用鼠标猛点击页面,你可以看到页面不断出面水波纹效果.然后水波纹渐渐消失.效果非常不错.我们一起看下效果图: 在线预览   源码下载 实现的代 ...

  8. 完成wamp安装后Mysql配置记录

    注意:下面如没有特殊说明,默认都是修改my.ini配置,修改完配置需要重启mysql服务. 1.安装完wamp后输入命令有乱码提示: 运行mysql命令时,出现的错误提示是乱码 :    ERROR ...

  9. 章鱼哥出品—VB.NET DataGridView绑定数据源 "与货币管理器的位置关联的行不能设置为不可见" 问题的解决

    DtaGridView绑定数据源后.假设想让数据条件显示的话,直接使用  My_Row.Visible = False就会出错.错误类型是 "与货币管理器的位置关联的行不能设置为不可见&qu ...

  10. Hadoop 回收站

    一.回收站简介: 在HDFS里,删除文件时,不会真正的删除,其实是放入回收站/trash,回收站里的文件可以快速恢复. 可以设置一个时间阀值,当回收站里文件的存放时间超过这个阀值或是回收站被清空时,文 ...