《CLR via C#》笔记:第4部分 核心机制(4)

  1.7 C#, 3.2 游戏引擎技术
  • 本博客所总结书籍为《CLR via C#(第4版)》清华大学出版社,2021年11月第11次印刷(如果是旧版书籍或者pdf可能会出现书页对不上的情况)
  • 你可以理解为本博客为该书的精简子集,给正在学习中的人提供一个“glance”,以及对于部分专业术语或知识点给出解释/博客链接。
  • 【本博客有如下定义“Px x”,第一个代表书中的页数,第二个代表大致内容从本页第几段开始。(如果有last+x代表倒数第几段,last代表最后一段)】
  • 电子书可以在博客首页的文档-资源归档中找到,或者点击:传送门自行查找。如有能力请支持正版。(很推荐放在竖屏上阅读本电子书,这多是一件美事)
  • 欢迎加群学习交流:637959304 进群密码:(CSGO的拆包密码) 

Odin插件了解一下–Unity

第二十四章 运行时序列化

  • 序列化是将对象或对象图转换成字节流的过程。反序列化是将字节流转换回对象图的过程。在对象和字节流之间转换是很有用的机制。(P541 1)
    一旦将对象序列化成内存的字节流,就可方便地以一些更有用的方式处理数据,比如进行加密和压缩。

序列化/反序列化快速入门

  • 代码示例:(P542 last)
//创建对象图以便把它们序列化到流中
var objectGraph = new List<String> { "Jeff","Kristin","Aidan","Grant" );stream stream = SerializeToMemory(objectGraph) ;
//反序列化对象,证明它能工作
objectGraph = (List<string>) DeserializeFromMemory(stream) ;foreach (var s in objectGraph) Console.writeLine(s) ;


private static MemoryStream SerializeToMemory(0bject objectGraph)
{
    //构造流来容纳序列化的对象
    Memorystream stream = new Memorystream ( ) ;
    //构造序列化格式化器来执行所有真正的工作
    BinaryFormatter formatter = new BinaryFormatter() ;
    //告诉格式化器将对象序列化到流中
    formatter.serialize (stream,objectGraph) ;
    //将序列化好的对象流返回给调用者
    return stream;
}
private static Object DeserializeFromMemory(Stream stream)
{
    //构造序列化格式化器来做所有真正的工作
    BinaryFormatter formatter = new BinaryFormatter ();
    //告诉格式化器从流中反序列化对象
    return formatter. Deserialize (stream) ;
}
  • 序列化对象图只需调用格式化器的Serialize方法,并向它传递两样东西:对流对象的引用,以及对想要序列化的对象图的引用。流对象标识了序列化好的字节应放到哪里,它可以是从System.IO.Stream抽象基类派生的任何类型的对象。也就是说,对象图可序列化成一个MemoryStream,FileStream或者NetworkStream等。(P543 last)
  • Serialize 的第二个参数是一个对象引用。这个对象可以是任何东西,可以是一个Int32,String,DateTime,Exception,List或者Dictionary等。objectGraph参数引用的对象可引用其他对象。例如,objectGraph可引用一个集合,而这个集合引用了一组对象。这些对象还可继续引用其他对象。调用格式化器的Serialize方法时,对象图中的所有对象都被序列化到流中。(P544 1)
  • 格式化器参考对每个对象的类型进行描述的元数据,从而了解如何序列化完整的对象图。序列化时,Serialize方法利用反射来查看每个对象的类型中都有哪些实例字段。在这些字段中,任何一个引用了其他对象﹐格式化器的Serialize方法就知道那些对象也要进行序列化。(P544 2)
  • 格式化器的算法非常智能。它们知道如何确保对象图中的每个对象都只序列化一次。换言之,如果对象图中的两个对象相互引用,格式化器会检测到这一点,每个对象都只序列化一次,避免发生死循环。(P544 3)
  • 请确保序列化和反序列化使用相同的格式化器。(P545 1)

使类型可序列化

  • 设计类型时,设计人员必须郑重地决定是否允许类型的实例序列化。类型默认是不可序列化的。(P546 1)
  • 使用System.SerializableAttribute进行定制特性以支持类型序列化。(P546 last)

控制序列化和反序列化

  • 序列化以及反序列化中一些问题:例如字段含有反序列化后变得无效的信息。字段含有很容易计算的信息,等。以及相应代码解决方法。(P548-P550)

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

  • 为了简化格式化器的操作,FCL在 System.Runtime.Serialization命名空间提供了一个FormatterServices类型。该类型只包含静态方法,而且该类型不能实例化。以下步骤描述了格式化器如何自动序列化类型应用了SerializableAttribute特性的对象:(P551 2)
    1、格式化器调用FormatterServices 的 GetSerializableMembers方法:
    2、对象被序列化,System.Reflection.MemberInfo对象数组传给FormatterServices 的静态方法 GetObjectData:
    3、格式化器将程序集标识和类型的完整名称写入流中。
    4、格式化器然后遍历两个数组中的元素,将每个成员的名称和值写入流中。
  • 格式化器如何自动反序列化类型应用了SerializableAttribute特性的对象:(P551 last)
    1、格式化器从流中读取程序集标识和完整类型名称。如果程序集当前没有加载到AppDomain中,就加载它。如果程序集不能加载,就抛出一个SerializationException异常,对象不能反序列化。如果程序集已加载,格式化器将程序集标识信息和类型全名传给FormatterServices 的静态方法 GetTypeFromAssembly。
    2、格式化器调用FormatterServices 的静态方法 GetUninitializedObject。这个方法为一个新对象分配内存,但不为对象调用构造器。
    3、格式化器现在构造并初始化一个MemberInfo 数组,具体做法和前面一样,都是调用FormatterServices的 GetSerializableMembers方法。这个方法返回序列化好、现在需要反序列化的一组字段。
    4、格式化器根据流中包含的数据创建并初始化一个Object数组
    5、将新分配对象、MemberInfo 数组以及并行Object 数组(其中包含字段值)的引用传给FormatterServices 的静态方法 PopulateObjectMembers。这个方法遍历数组,将每个字段初始化成对应的值。

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

  • 为了对序列化/反序列化的数据进行完全的控制,并避免使用反射,你的类型可实现System.Runtime.Serialization.ISerializable接口。(P552 last2)
  • ISerializable接口具体实现(P553-P558)

流上下文

  • 在一些比较少见的情况下,一个对象可能想知道它要在什么地方反序列化,从而以不同的方式生成它的状态。例如,如果对象中包装了Windows信号量(semaphore)对象,如果它知道要反序列化到同一个进程中,就可决定对它的内核句柄(kernel handle)进行序列化,这是因为内核句柄在一个进程中有效。但如果要反序列化到同一台计算机的不同进程中,就可决定对信号量的字符串名称进行序列化。最后,如果要反序列化到不同计算机上的进程,就可决定抛出异常,因为信号量只在一台机器内有效。(P558 last2)
  • StreamingContext(流上下文):接受一个StreamingContext结构的方法能检查State属性位的标志,判断要序列化/反序列化的对象的来源或目的地。(P558 last)
image 78 - 《CLR via C#》笔记:第4部分 核心机制(4)
公共只读属性
image 79 - 《CLR via C#》笔记:第4部分 核心机制(4)
State标志

类型序列化为不同类型以及对象反序列化为不同对象

  • 如何设计类型将自己序列化或反序列化成不同的类型或对象。(P559-P561)

序列化代理

  • 格式化器还允许不是“类型实现的一部分”的代码重写该类型“序列化和反序列化其对象”的方式。应用程序代码之所以要重写(覆盖)类型的行为,主要是出于两方面的考虑:(P561 2)
    1、允许开发人员序列化最初没有设计成要序列化的类型。
    2、允许开发人员提供一种方式将类型的一个版本映射到类型的一个不同的版本
  • 为了使这个机制工作起来,首先要定义一个“代理类型”(surrogate type),它接管对现有类型进行序列化和反序列化的行动。然后,向格式化器登记该代理类型的实例,告诉格式化器代理类型要作用于现有的哪个类型。一旦格式化器要对现有类型的实例进行序列化或反序列化,就调用由你的代理对象定义的方法。(P562 2)
  • 代理选择器链-多个SurrogateSelector对象可链接到一起。(P564 last)

反序列化对象时重写程序集/类型

  • 序列化对象时,格式化器输出类型及其定义程序集的全名。反序列化对象时,格式化器根据这个信息确定要为对象构造并初始化什么类型。
  • 利用System.Runtime.Serialization.SerializationBinder类,可以非常简单地将一个对象反序列化成不同类型。P566 3()

第二十五章 与WinRT组件互操作

  • CLR投射与WinRT组件类型系统规则,框架投射,用C#定义WinRT组件(P569-P588)(与Unity关联度不高,故不做细致总结)

LEAVE A COMMENT