1. 程式人生 > >Unity的序列化和反序列化過程解析

Unity的序列化和反序列化過程解析

Unity的序列化和反序列化過程解析

序列化是將物件轉換為位元組流的過程

序列化物件只需要呼叫格式化器BinaryFormatter的Serialize方法。Serialize方法到底是如何進行序列化的呢?首先,格式化器會參考目標物件的型別的元資料,進而瞭解要序列化的物件的資訊。具體來說,使用了反射機制。

格式化器序列化的流程

以序列化一個英雄類Hero為例:

[Serializable]
public class Hero{
    int maxHp;
    int currentHp;
    float Attack;
    public Hero(){
        ...
    }
    public void Attack(){
        ...
    }
}

第一步:獲取目標物件的成員欄位資訊

首先呼叫FormatterServices類的GetSerializableMembers方法,該方法簽名如下:

public static MemberInfo[]GetSerializableMembers(
  Type type,
  StreamingContext context
)

型別Type表示正在序列化的型別,此例中為Hero類,即獲取一個類的元資料。元資料為描述資料的資料,即該類有哪些成員變數以及對應的型別是什麼,例如Hero類有2個int型成員變數和一個float型成員變數。

SteamingContext引數表示發生序列化的上下文。

返回型別為MemberInfo[],MemberInfo對應可以被序列化的例項欄位,本例中是int,int,float。

第二步:獲取目標物件的成員欄位的值

當獲得了由MemberInfo物件所構成的陣列後,就進入了物件被序列化的階段。此時格式化器要呼叫FormatterServices類的另一個靜態方法,即GetObjectData,該方法的簽名如下所示。

public static Object[]GetObjectData(
  Object obj,
  MemberInfo[]members
)

該方法通過MemberInfo陣列去類的例項obj中將對應的成員變數的例項提取出來,一一對應地生成至返回值Object陣列中,返回值Object中的每個變數都是物件obj中對應的成員變數的例項。

第三步:程式集標識和型別的完整名稱寫入流

第四步:成員變數的值寫入流

格式化器接下來會遍歷在第一步和第二步得到的兩個陣列以獲得成員名稱和與其對應的值,最後將這些資訊也寫入流中。

格式化器反序列化的流程

基本與序列化的流程一一對應:

  1. 先從流中讀取程式集標識和完整的型別名稱;
  2. 然後為新物件obj分配一塊記憶體空間,此時還沒有呼叫建構函式;
  3. 再一次使用前文中提到的FormatterServices類的GetSerializableMembers方法(可見類的成員欄位的資訊並不需要寫入到流中,而是動態地去獲取),得到一個MemberInfo陣列;
  4. 格式化器根據流中包含的資料建立一個Object陣列;
  5. 根據MemberInfo陣列和Object陣列為物件obj進行初始化。

序列化和反序列化與程式集的關係

當代碼在對物件進行序列化時,寫入流的內容之中還包括型別的全名以及型別定義程式集的全名。而在反序列化時,格式化器首先獲取的也是程式集的標識資訊,然後再通過呼叫System.Reflection.Assembly的Load方法將目標程式集載入進入當前的AppDomain中。只有當程式集載入完成後,格式化器才能夠在程式集中查詢需要被反序列化的物件的型別資訊。一旦找到符合要求的型別,接下來便是建立該型別的例項,然後再從流中獲取和該例項欄位相對應的值為該例項的欄位賦值。

參考

《Unity 3D 指令碼程式設計 使用C#語言開發跨平臺遊戲》第十章