细说C#中的系列化与反系列化的基本原理和过程
虽然我们平时都使用第三方库来进行序列化和反序列化,用起来也很方便,但至少得明白序列化与反序列化的基本原理。
懂得人就别看了!
注意:从.NET Framework 2.0 开始,序列化格式化器类SoapFormatter已过时。请改用 BinaryFormatter。
- 序列化:把目标对象转换为字节流的过程
- 反序列化:把字节流转换回对象的过程
序列化与反序列化需要一个序列化器类:即System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
类
实例化一个格式化器BinaryFormatter类:BinaryFormatter binaryFormatter = new BinaryFormatter();
一、序列化:
binaryFormatter.Serialize(Stream stream, Object obj);
- 该方法把一个或多个目标对象序列化为字节流并保存到目标流对象stream中。需要提供一个目标流对象stream(stream对象可以是基类Stream的派生类:
FileStream、MemoryStream、NetWorkStream
等流对象);
序列化过程:
- 序列化时,首先判断每个对象的类型定义是否应用了可序列化
[Serializable]
特性,否则抛出异常。 - 其次调用对象中那些被标记了
[OnSerializing]
特性的所有方法。(即:执行序列化前,先调用该方法做一些事情) - 接下来,利用反射机制来取得每个目标对象的类型中所有需要序列化的实例字段的信息,并读取对应字段的值保存到字节流中。
- 序列化时,还保存了目标类型的全名、定义类型程序集的全名,作为标识信息保存到流中(用于反序列化)。
- 最后,调用所有被标记了
[OnSerialized]
特性的方法。(即:序列化完成后,调用该方法做一些事情)
二、反序列化:
SomeObject obj = (SomeObject)binaryFormatter.Deserialize(Stream stream);
- 该方法把目标流对象stream中的字节流反序列化为Object对象,可根据需求进行转型为对应的目标对象
反序列化过程:
- 反序列化时,首先从字节流中读取程序集的标识信息,然后调用
System.Reflection.Assembly
类的Load()
方法加载该程序集到当前的AppDomain
中,只有当程序集加载成功后,格式化器才能在程序集中查找是否存在与需要被反序列化的对象的类型信息相同的类型 - 找到类型后,调用对象中那些被标记了
[OnDeserializing]
特性的所有方法 - 接下来利用该类型创建实例
- 然后从字节流中获取对应字段的值对该实例进行初始化。
- 最后,调用所有被标记了
[OnDeserialized]
特性的方法。
此过程中若找不到匹配的类型,则会抛出异常终止反序列化。但是,我在利用第三方类库反序列化JSON文件时,JSON文件并不存在对象名和程序集名的标识信息,第三方类库的格式化器应该是根据各个对象的成员名称、类型,在当前AppDomain
中的所有程序集中进行查找匹配的类型。
注意:
- 最好把序列化或反序列化的过程放进try块中,用catch (SerializationException e)块处理需要处理的异常,并在finally块中释放资源(关闭流)。
- 可以把流的定义放在using语句的“()”中,把序列化反序列化代码放在using块内,来达到自动释放资源的目的。
三、序列化配置
在执行序列化和反序列化之前,可以对格式化器的Context
属性进行设置,Context
属性是一个StreamingContext
结构
binaryFormatter.Context = new StreamingContext(StreamingContextStates.Remoting);
//指定为来源和目的地是远程的
StreamingContext
结构的属性有两个:
State
属性,是一个枚举类型StreamingContextStates
的值,用来说明序列化和反序列化的对象的来源和目的地Context
属性,一个上下文对象的引用,包含了用户希望得到的任何上下文信息
StreamingContext
结构存在的意义就是通过State
属性的值描述给定的序列化流的源和目标,并利用Context
属性提供一个由调用方定义的附加上下文。
(因为同一个被序列化好的对象可能会有不同的目的地,如不同机器,不同进程中等,我们就可以通过State属性的状态来标识对象的目的地)
示例:利用序列化和反序列化,定义一个深度克隆一个对象的方法
public object DeepCloneObject(object oldObj)
{
try
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Context = new StreamingContext(StreamingContextStates.Clone);
//把对象序列化到流中
bf.Serialize(stream, oldObj);
//在进行反序列化前,需要先定位到内存流的起始位置
stream.Position = 0;
//将内存流中的内容反序列化成新的对象
return bf.Deserialize(stream);
}
}
catch(SerializationException e)
{
Console.WriteLine("序列化和反序列化时出错了,错误信息为:" + e.Message);
//不做处理,重新抛出原异常对象
throw;
}
finally
{
//using中的stream对象会被自动释放,这里不要对它处理
}
}
四、如果有些类型的成员无法进行序列化或反序列化,则可以为该类型实现接口ISerializationCallbackReceiver
,该接口定义了两个方法:
比如在Unity3d中,无法序列化枚举类型的成员,就需要把他标记为[NonSerialized]
不对他处理,然后定义一个该成员的字符串形式的成员,通过该字符串和对应枚举类型的转换,就可以达到序列化的目的了。如下代码:
public enum ItemType
{
left,
right
}
public class ScoreModel:ISerializationCallbackReceiver
{
public int Score { get; set; }
[NonSerialized]
public ItemType itemType;
public string itemTypeString;
//反序列化完成自动后调用
public void OnAfterDeserialize()
{
itemType = (ItemType)Enum.Parse(typeof(ItemType), itemTypeString);
}
//进行序列化之前自动调用
public void OnBeforeSerialize()
{
itemTypeString = itemType.ToString();
}
}
五、关于序列化为派生类类型的情况
即使是第三方类库,可能也无法处理这种情况,比如:从JSON中反序列化后得到的对象的成员是一个基类类型的,如果直接把该成员强转为我们实际需要的子类类型,正常情况下是不行的,这时就需要自定义一些方法来实现了,利用协变在序列化过程中返回子类类型的对象给基类类型,这样得到的基类类型就可以强转为子类类型了。具体实现代码,大家可以搜起来!
细说C#中的系列化与反系列化的基本原理和过程的更多相关文章
- Java Serializable系列化与反系列化
[引言] 将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接 ...
- C#对泛型List<T>系列化与反系列化
练习一个小例子,在C#中,怎样对泛型List<T>数据集进行系列化与反系列化.我们先了解msdn提供的JavaScriptSerializer类: JavaScriptSerializer ...
- 细说.NET 中的多线程 (一 概念)
为什么使用多线程 使用户界面能够随时相应用户输入 当某个应用程序在进行大量运算时候,为了保证应用程序能够随时相应客户的输入,这个时候我们往往需要让大量运算和相应用户输入这两个行为在不同的线程中进行. ...
- 细说.NET中的多线程 (二 线程池)
上一章我们了解到,由于线程的创建,销毁都是需要耗费大量资源和时间的,开发者应该非常节约的使用线程资源.最好的办法是使用线程池,线程池能够避免当前进行中大量的线程导致操作系统不停的进行线程切换,当线程数 ...
- [Asp.net]c#中的斜杠和反斜杠
引言 在外地出差,给客户部署项目,三家做的项目要在一起集成,这就造成数据格式不同,路径中的斜杠和反斜杠造成了很大的问题. 查了一下这方面的资料,这里做一些记录,算是一个小结吧. 正斜杠(/)与反斜杠( ...
- 常用路径 URL 中的斜杠与反斜杠
常用路径中的斜杠与反斜杠... ------------------------------ 斜杠:反斜杠:======================电脑能识别的斜杠有两种:斜杠分正斜杠(forwa ...
- Oracle 11G R2 RAC中的scan ip 的用途和基本原理【转】
Oracle 11G R2 RAC增加了scan ip功能,在11.2之前,client链接数据库的时候要用vip,假如你的cluster有4个节点,那么客户端的tnsnames.ora中就对应有四个 ...
- APS中生产计划排程模块的基本原理
高级计划系统(APS)作为ERP和MES的补充,用于协调物流.开发瓶颈资源和保证交货日期. APS包括需求和供应计划.运输和生产计划排程等各种供应链计划模块,本文主要介绍APS中生产计划排程模块的基本 ...
- idea在maven中引入了jar包依赖,但是编译过程中报出XXX程序包不存在,已解决
idea在maven中引入了jar包依赖,但是编译过程中报出XXX程序包不存在 1. 报错具体情况 2. Project Structure中的Libraries没有任何红色波浪线 3. 发现自己要引 ...
随机推荐
- C# 快速高效率复制对象另一种方式 表达式树
1.需求 在代码中经常会遇到需要把对象复制一遍,或者把属性名相同的值复制一遍. 比如: public class Student { public int Id { get; set; } publi ...
- Java环境----JDK开发环境搭建及环境变量配置
1.啥是jdk? jdk的是java development kit的缩写,意思是java程序开发的工具包. jdk的版本,一共有三种,标准版(j2se),企业版(j2ee),移动设备版(j2me). ...
- 关于苹果真机 getFullYear()返回值为NAN的问题
问题描述: 在html页面中获得后台传过来的一个时间并显示在页面上,我用getFullYear() ,getMonth(),getDate()分别获得了年月日在电脑上和三星手机上页面都能正确的显示时间 ...
- children 和childNodes 的区别
1:childNodes /children相同点:它返回指定元素的子元素集合. 2:区别:children : 它是非标准的,仅返回HTML节点.甚至不返回文本节点.所有浏览器表现一 致. chi ...
- 为部署ASP.NET Core准备:使用Hyper-V安装Ubuntu Server 16.10
概述 Hyper-V是微软的一款虚拟化产品,和VMWare一样采用的hypervisor技术.它已经被内嵌到Win10系统内,我们只需要进行简单的安装即可.但是前提是要确保你的机器已经启用虚拟化,可以 ...
- Elasticsearch1.7服务搭建与入门操作
ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...
- ue4竖排文本显示
最近发现中国风游戏中,经常会遇到旁白文字竖着显示的需求. 于是我首先找了找控件蓝图中的text有没有相关类似横竖文本框的选项,然而并无所获. 突然间灵机一动! 竖着显示不就是每个字一换行嘛! 说干就干 ...
- java面试题—精选30道Java笔试题解答(二)
摘要: java面试题-精选30道Java笔试题解答(二) 19. 下面程序能正常运行吗() public class NULL { public static void haha(){ System ...
- hdu1512 Monkey King
Problem Description Once in a forest, there lived N aggressive monkeys. At the beginning, they each ...
- ssh整合时报出的异常及解决办法
com.opensymphony.xwork2.inject.DependencyException: com.opensymphony.xwork2.inject.ContainerImpl$Mis ...