热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:CLR序列化

篇首语:本文由编程笔记#小编为大家整理,主要介绍了CLR序列化相关的知识,希望对你有一定的参考价值。序列化的概念

篇首语:本文由编程笔记#小编为大家整理,主要介绍了CLR 序列化相关的知识,希望对你有一定的参考价值。


序列化的概念

序列化 是将对象或对象图转化成字节流的过程。

反序列化 是将字节流转换回对象图的过程。

序列化的好处是,一旦将对象序列化成内存中的字节流,就可以方便的以一些更有用的方式处理数据,比如:



  • 应用程序的状态可保存到磁盘文件或数据库中,并在应用程序下次允许时恢复。



  • 一组对象可轻松通过网络发送给另一台机器上运行的程序。



.net 可以支持二进制序列化、XML和SOAP 序列化、JSON 序列化。其中二进制序列化功能最强大,类型保真,XML只序列化公共属性和字段,JSON 只序列化公共属性。JSON 广泛用于web 上共享数据,是一个开放式标准。

本篇只介绍.net 中二进制序列化。

如何使类型可序列化

看一个序列化应用的例子:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
internal static class QuickStart {
   public static void Main() {
       // Create a graph of objects to serialize them to the stream
       var objectGraph = new List<String> { "Jeff", "Kristin", "Aidan", "Grant" };
       Stream stream = SerializeToMemory(objectGraph);
       // Reset everything for this demo
       stream.Position = 0;
       objectGraph = null;
       // Deserialize the objects and prove it worked
       objectGraph = (List<String>) DeserializeFromMemory(stream);
       foreach (var s in objectGraph) Console.WriteLine(s);
  }
?
   private static MemoryStream SerializeToMemory(Object objectGraph) {
       // Construct a stream that is to hold the serialized objects
       MemoryStream stream = new MemoryStream();
       // Construct a serialization formatter that does all the hard work
       BinaryFormatter formatter = new BinaryFormatter();
       // Tell the formatter to serialize the objects into the stream
       formatter.Serialize(stream, objectGraph);
       // Return the stream of serialized objects back to the caller
       return stream;
  }
?
   private static Object DeserializeFromMemory(Stream stream) {
       // Construct a serialization formatter that does all the hard work
       BinaryFormatter formatter = new BinaryFormatter();
       // Tell the formatter to deserialize the objects from the stream
       return formatter.Deserialize(stream);
  }
}

格式化器是实现了 System.Runtime.Serialization.IFormatter 接口的类型,它知道如何序列化和反序列化对象图。BinaryFormatter 就是FCL 提供的格式化器。

BinaryFormatter 格式化器的Serialize 方法将对象转为流数据,它的Deserialize 方法将流数据转为对象。

格式化器的Deserialize 方法检查流的内容,构造流中所有对象的实例,并初始化所有这些对象中的字段。

反序列化的过程是,格式化器首先获取程序集标识信息,并通过调用System.Reflection.Assembly 的load 方法,确保程序集已加载到正在执行的AppDomain中。如果程序集中没有反序列化实例的类型就会报错。

下面是一个利用序列化实现对象的深拷贝:

private static Object DeepClone(Object original) {
   // Construct a temporary memory stream
   using (MemoryStream stream = new MemoryStream()) {
       // Construct a serialization formatter that does all the hard work
       BinaryFormatter formatter = new BinaryFormatter();
       // This line is explained in this chapter‘s "Streaming Contexts" section
       formatter.Context = new StreamingContext(StreamingContextStates.Clone);
       // Serialize the object graph into the memory stream
       formatter.Serialize(stream, original);
       // Seek back to the start of the memory stream before deserializing
       stream.Position = 0;
       // Deserialize the graph into a new set of objects and
       // return the root of the graph (deep copy) to the caller
       return formatter.Deserialize(stream);
  }
}


  • 注意要保证代码为序列化和反序列化使用相同的格式化器。



  • 可将多个对象图序列化到一个流中。



  • 在反序列化时要注意当前AppDomain中有对应的类型。



 

下面说一说如何让类型可序列化

很简单,像下面这样向类型应用定制特性 System.SerializableAttribute 。

[Serializable]
internal struct Point {public Int32 x, y;}


  • SerializableAttribute 这个定制特性只能应用于引用类型 class、值类型 struct、枚举类型 enum、和委托 delegate。



  • 枚举类型和 委托类型总是可序列化的,不必显示应用特性。



  • SerializableAttribute 不会被派生类型继承。



 

如何控制序列化和反序列化

将SerializableAttribute 定制特性应用于类型,所有实例字段都会被序列化。如果我不想让某一个字段序列化怎么办呢?。还有哪些情况不应该序列化呢?



  • windows 内核对象的句柄,它在反序列化到另一个进程或另一台机器上就会失去意义。



  • 字段含有很容易计算的信息,去掉它可以减少传输的数据,增强性能。



使用 System.NonSerializedAttribute 定制特性指出类型中不应该序列化的字段。

[Serializable]
internal class Circle{
private Double m_radius; //半径

[NonSerialized]
private Double m_area; //面积

public Circle(Double radius){
m_radius = radius;
m_area = Math.PI * m_radius * m_radius;
}
...
}

除了NonSerializedAttribute 定制特性之外,FCL 还提供了其他控制序列化和反序列化的特性。这些特性应用于不同的场景,我这里只简单列举一些常用的。

System.Runtime.Serialization //这个命名空间有好多其他特性
OnDeserializedAttribute //根据字段值初始化瞬时状态
OnSerializingAttribute //序列化前,修改任何需要修改的状态
OnSerializedAttribute  //序列化后,恢复任何需要恢复的状态
OnDeserializingAttribute //在类型新版本中为字段设置默认值

 

格式化器如何序列化类型实例

FCL在 System.Runtime.Serialization 命名空间提供了一个FormatterServices 类型。该类型只包含静态方法,而且该类型不能实例化。以下步骤描述了格式化器如何自动序列化类型,应用了 SerializableAttribute 特性的对象。



  • 1 格式化器调用 FormatterServices 的 GetSerializableMembers方法:

    通过反射获取public 和private实例字段,将它们放到MemberInfo 对象构成的数组中。



  • 2 对象被序列化,System.Reflection.MemberInfo 对象数组传给 FormatterServices静态方法GetObjectData:



  • 3 格式化器将程序 集标识和类型的完整名称写入流中。



  • 4 格式化器遍历两个数组中的元素,将每个成员的名称和值写入流中。



以下步骤描述格式化器如何自动反序列化类型应用了SerializableAttribute 特性的对象。



  • 格式化器从流中读取程序集标识和完整类型名称。将该程序集加载到AppDomain中。



  • 格式化器调用 FormatterServices 的静态方法GetUninitializedObject:

    这个方法为新对象分配内存,但不为对象调用构造器。



  • 格式化器现在构造并初始化一个MemberInfo 数组,具体做法和前面一样,都是调用 FormatterServices的 GetSerializableMembers方法。



  • 格式化器根据流中包含的数据创建并初始化一个Object 数组。



  • 将新分配对象、MemberInfo数组以及并行Object数组的引用传给FormatterServices的静态方法 PopulateObjectMembers:

    这个方法遍历数组,将每个字段初始化成对应的值,到此为止,对象就算是被彻底反序列化了。



 

控制序列化/反序列化的数据

如果前面提到的特性:OnSerialized、OnDeserializing、OnDeserialized等,它们不能满足你想要的功能。另外格式化器使用的是反射,反射会影响程序的性能。为了对序列化/和反序列化的数据进行完全的控制,避免使用反射,你的类型可以实现 System.Runtime.Serialization.ISerializable 接口。它的定义如下:

public interface ISerializable {
void GetObjectData(SerializationInfo info , StreamingContext context);
}

它只有一个方法GetObjectData,实现这个接口的大多数类型还实现了一个特殊的构造器。

类型一旦实现了这个接口,所有派生类型也要实现它,而且派生类型必须保证调用基类的GetObjectData方法和特殊构造器。

 

格式化器序列化对象图时会检查每个对象。如果发现一个对象的类型实现了 ISerializable接口,就会忽略所有定制特性,改为构造新的System.Runtime.Serialization.SerializationInfo 对象。该对象包含了要为对象序列化的值的集合。

 

要实现ISerializable 但基类没有实现怎么办?

这种情况下,派生类必须手动序列化基类的字段,具体的做法就是获取它们的值,并把者些值添加到SerializationInfo 集合中。然后,在你的特殊构造器中,还必须从集合中取出值,以某种方式设置基类的字段。如果基类的字段是public 或 protected 的可以实现,但是private 字段,几乎不能实现。

 

流上下文

一组序列化好的对象可以有许多目的地:同一个进程、同一台机器的不同进程、不同机器上的不同进程等。

某些情况下,一个对象想知道它要在什么地方反序列化,从而以不同的方式生成它的状态。

例如,如果对象中包装了Windows 信号量对象,如果它知道要反序列化到同一个进程中,就可决定对它的内核句柄进行序列化,这是因为内核句柄在一个进程中有效。 但如果要反序列化到同一台计算机的不同进程中,就可决定对信号量的字符串名称进行序列化。 最后,如果要反序列化到不同计算机的进程,就可决定抛出异常,因为信号量只在一台机器中有效。

格式化器中的大量方法都接受一个StreamingContext 类型,StreamingContextStates 结构是一个非常简单的值类型,它只提供两个公共只读属性。

技术图片

 

 

接受一个StreamingContext 结构的方法能检查State 属性的位标志,判断要序列化/反序列化的对象的来源或目的地。

IFormatter 接口定义了StreamingContext类型的可读/可写性Context。构造格式化器时,格式化器会初始化它的 Context属性,将StreamingContextStates 设为All,对额外状态对象的引用设为null。

 

.net 由于元数据和IL 中间语言以及JIT 编译等特点使它在序列化方面有更多的灵活性。在.net 中类型还能序列化为不同类型,对象也能反序列化为不同的对象。.net 中的格式化器还允许不是 “类型实现的一部分” 的代码重写该类型 “序列化和反序列化其对象” 的方式。


推荐阅读
  • 这篇文章将为大家详细讲解有关C#开发技巧有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。C#开发技 ... [详细]
  • C#按值复制数组我有一个类型化的数组MyType[]types;我想制作这个数组的独立副本。我试过这个MyType[]types2newMyType[types.Length];t ... [详细]
  • 接口自动化相关面试题
    你好,我是懂Java的测试最近辅导简历,有同学向我反馈,自学过接口自动化、没有落地接口自动化项目办?还有很多同学落地实践过自 ... [详细]
  • java中如何对汉字进行排序?
    前言:上一篇文章我们讲解了java中实现Comparator进行排序和实现Comparable进行排序,我们分别举例根据学号和姓名降序排序, ... [详细]
  • Android性能优化检测App卡顿
    在移动APP性能评测-流畅度评测中,我们介绍了如何准确客观评价APP的流畅度,最终采用SM指标来评价应用的流畅度,在知道如何评价流畅度之后 ... [详细]
  • C#|类型。GetTypeHandle()方法原文:https ... [详细]
  • 找出字符串中重复字符
    2019独角兽企业重金招聘Python工程师标准packagejavaBasic;importjava.util.HashMap;importjava.util.Map; ... [详细]
  • 我正在使用数组列表通过构建一个交互式菜单供用户选择来存储来自用户输入的值。到目前为止,我的两个选择是为用户提供向列表输入数据和读取列表的全部内容。到目前为止,我创建的代码由两个类组成。 ... [详细]
  • Shiro 简单了解
    Shiro简单了解简单用过SpringSecurity安全框架后,再试试另一个安全框架——Shiro。1.Shiro简介ApacheShiro是一个强大且易用的Java安全框架:S ... [详细]
  • #import挂载对象所需要的参数(UIAlertView挂载对象)staticconstcharkRepresente ... [详细]
  • 调用:视图调用:1@Html.DropDownListFor(tt.HrEmpGuid,ViewData[Emp] as SelectList, new {@class   ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了在单独的JVM上执行新的JavaFX应用程序相关的知识,希望对你有一定的参考价值。 ... [详细]
  • MaximumXORofTwoNumbersinanArrayGivenanon-emptyarrayofnumbers,a0,a1,a2,…,an-1,where0≤ai ... [详细]
  • 每次用到v-charts我都一阵头疼,因为明明是相同的功能,但是我好像每次用到的解决方法都不一样??每次都是在api中各种查,各种尝试…直到做了个各种数据图形的需求,决定还是好好整 ... [详细]
  • 使用Java调用exe可执行文件
    一、出发点平日里,我们看到了很多已经成型的可执行文件,而且经过了一定的封装。因为开源的关系,大多时候可以自己使用eclipse进行编译&# ... [详细]
author-avatar
BB_KYLE
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有