主要介绍如何使用序列化与反序列化、控制类型的序列化与反序列化、Unity中的序列化与反序列化、序列化器是如何工作的。
一、如何使用序列化与反序列化
序列化:将数据(可以是类,结构体还有其他int型等数据)转换成字节流。
反序列化:将字节流文件转换成原数据类型(如类等)。
序列化与反序列化的作用:
1.可以将类的信息转换成字节流用于网络传输
2.一般的引用类型的数据复制,只能复制引用,但是使用序列化与反序列化就可以复制对象实例本身。
3.用于数据保存
序列化:
首先我们有一个需要被序列化的数据,如类A,我们需要在它声明的前一行添加[Serializable]定制特性标志。
然后我们新建一个用来装字节流的容器:MemoryStream ms = new MemoryStream ();
然后我们新建一个把类和字节流互相转化的格式化器:BianryFormatter bf=new BianryFormatter ();
然后我们用格式化器把类转化成流存储在容器中:bf.Serizalize(bf,ms);
反序列化:
新建一个可以把类和字节流互相转化的格式化器:BinaryFormatter bf=new BinaryFormatter ();
从刚刚的容器中取出类:A=bf.Deserialize(ms);
序列化时,一个对象只能被转换一次,但是如果该对象为null,则会触发死循环,一直转换下去,但是Unity为了避免死循环,所以设置了一个上限729次。
序列化时,一个容器中可以装多个对象的字节流,当然也可以从容器中取出多个对象。
被转换的数据,是任何继承自System.Object的数据类型,所以也可以是List<Object>之类的,那么List中的所有对象都会被转换成字节流。
容器MemoryStream 只是把数据存储在内存中,FileStream fs=new FileStream ("fileName.dat",FileMode.Create);可以把数据存储成dat文件。
格式化器除了BinaryFormatter 还有SoapFormatter,但是两者不能配合使用。
二、序列化与反序列化前后需要调用函数?
一个数据希望能被序列化,需要在它声明的前一行添加[Serializable]定制特性标志。这个特性只能作用于引用类型与值类型、枚举类型、委托类型。因为后两者始终可以被序列化,所以不需要添加标志。
基类添加了该标志,子类不会继承该标志。
当一个类中有部分变量不需要被序列化,可以通过标志[NonSerialized]来标记。这样这个值就不会被写入字节流。但是当从字节流中读取这个类时,因为相当于复制了一个实例,没被序列化的变量将会被赋值为默认值。
如果我们想在复制实例时,给没被序列化的变量赋值,可以在读取类后,调用一些函数,只要这个函数声明前一行写了[OnDeserialized],还有函数参数必须只有一个StreamContext。
StreamContext是一个值类型,它里面比较重要的有:
StreamContextStates State:需要操作的对象的来源与目的地,比如是不是同一进程,在不在一台机器上等等。
Object Context:需要操作的对象。
除了可以在读取数据之后调用函数,还有一些特性可以设置函数的调用时间:
[OnSerializing]:格式化器在格式化数据前,会先调用该函数
[OnSerialized]:格式化数据后调用
[OnDeserializing]:还原数据前调用。
三、Unity中的序列化与反序列化
1. Inspector监视板:我们可以在这个属性面板中看到很多可以编辑的公共变量或者其他数值,其实并不是Unity临时调用游戏脚本中的C#接口获取的,而是通过被观察的对象反序列化它自己而得到的。
2. 预制体prefab:当生成预制体时,就是序列化对象,复制prefab成对象就是反序列化的过程。prefab其实是一种文件格式,它可以是“Force text serialization” 的文本格式,也可以是“Force Binary”的二进制格式。
说到复制prefab成对象就要提到通过函数Instantiate动态地复制对象。Instantiate有两种签名:
public static object Instantiate(Object obj);
public static object Instantiate(Object obj, Vector3 position,Quaternion rotation);
第一种方式因为没有指定位置和旋转角度,所以默认在Vector3.zero处生成
3.场景文件.unity,它也是通过序列化之后的文件,不同形式为文本型的YMAL与二进制型的。那么载入场景就是反序列化的过程。
4.垃圾回收机制,与C#的垃圾回收机制不同,这里主要是指,新场景加载后,旧场景的素材回收,主要通过Resource.GarbageCollectSharedAssets()方法,该方法内部也是利用了序列化机制回收的。
Unity3D提供的序列化系统支持自定义类型的多态,如将dog/cat/giraffe都添加到animals的数组中,被序列化,但是读取数据时,只会反序列出三个animal的对象(不知道为什么这样还叫支持多态)。
因为序列化不能很好的序列值为null的数据,比如有很多没有子节点的树结构,就不能很好地被序列化,这时需要通过一个中间数据结构,将树转换成没有Null的List,再进行序列化。
四、序列化器是如何工作的
序列化:
1. 将可以被序列化的实例字段处理成一个数组(比如将List中的每一个对象组成一个数组)
2.获得每个对象中需要被序列化的成员及值。
3. 将程序集标志(猜测是一个表示对象类型的标志)和每个成员的类型写入流
4.将每个成员变量的变量名及值写入流。
反序列化:
1. 读取程序集标志与成员类型(已经知道有这些类,并且每个类有哪些成员)
2.在内存上为新的对象分配空间(由上一步的信息就可以知道要多少空间)
3.构造并初始化一个字段数组(就是很多变量组成的数组)
4.获取字段所对应的数值信息
5.根据字段信息,初始化新对象。