(轉)Unity實現c#熱更新方案探究(三)

轉載請標明出處:http://www.cnblogs.com/zblade/ 

前面兩篇文章從頭到尾講解了C#熱更新的一些方案,從程序域來加載和卸載DLL,到使用ILRuntime來實現安卓和IOS平臺的DLL熱更新。文章二中講解了ILRuntime對於IL虛擬機在加載DLL的過程中的一些解構。那麼今天收尾的文章,就來講解一下如何基於這個虛擬機實現對於類,方法,屬性的調用。

一、基於appDomain來加載類實現反射的調用

對於ILRuntime中的反射,大概可以分爲三種類型:一種是ILRuntime運行的DLL中,該程序集中的類之間的反射,這是基於C#的反射,應用無差別; 一種是unity遊戲主工程中對DLL中類的反射,這樣的反射可以分爲兩種,一種是獲取類,然後直接調用方法, 一種是基於appDomain包裝的反射,下面分別舉例兩種unity主工程對DLL中類的反射,來研究一下如何實現這個過程。

1、基於LoadedTypes來實現反射方法的調用

在ILRuntime中,不能基於System.Type來直接獲取熱更新DLL中的類,只有基於唯一的appDomain實例,基於LoadedTypes這種來獲取熱更新中的DLL,基於代碼來分析,更爲詳細:

首先,加載獲取該DLL中的指定類:

var it = appDomain.LoadedTypes["HotFix_Project.InstanceClass"]

跟蹤LoadedTypes:

public Dictionary<string, IType> LoadedTypes{get{return mapType.InnerDictionary;}}

跟蹤看mapType.InnerDictionary:

ThreadSafeDictionary<string, IType> mapType = new ThreadSafeDictionary<string, IType>();

這個mapType是什麼時候裝配的?

來自於文章二中的LoadAssembly的後續操作:

那麼這個module.GetTypes是如何操作的?

分別基於協程來return type以及其nestedTypes,關鍵是看Types是怎麼獲取的:

關鍵是read操作:

繼續跟進Read操作:

關鍵是:

var mtypes = metadata.Types

後續都是對其的封裝和填充,對於metadata的填充,來自於InitializeTypeDefinitions這個操作:

關鍵操作是ReadType這個操作:

構建一個內部定義的類,然後做數據填充,看看關鍵的幾個屬性的設置:BaseType ,設置其父類型,fieldsrange/methods_range 是對屬性範圍和方法範圍的設置:

所以基本方法還是ReadListRange:

在這兒,我們最終回到了文章二中對於IL虛擬機中的tableHeap的引用,最後實現了和文章二的首尾呼應。

好了,收起思緒,回到最開始的,獲取類,這樣獲得的一個類,這樣得到的一個類,繼承自IType,在Unity主工程中,則需要System.Type才能繼續使用反射接口,其對於的封裝來自昱這個ILType封裝的ReflectionType, 其中的ILRuntimeType繼承自Type類:

基於其,可以直接調用System.Type的GetConstructor方法,構建實例,歸併幾個代碼,可以表示爲(直接使用的實例源代碼):

var it = appDomain.LoadedTypes["HotFix_Project.InstanceClass"];
var type = it.ReflectionType;
var ctor = type.GetConstructor(new System.Type[0]);
var obj = ctor.Invoke(null);

對應可以得到DLL中該類的構造函數的調用:

2、基於appDomain內嵌的Invoke來實現反射

在ILRuntime中,在appDomain中內嵌了一套Invoke的實現,可以在Unity工程中直接調用來實現對熱更新DLL中類的方法的調用:

關鍵操作就是2步: GetType和 GetMethod,獲取類型的過程,和前面有點類似,就是對mapType中存儲的獲取,如果沒有,則進行查找和填充,這兒重點說說方法是如何獲取的:

粗看就是從methods中取出來,做相應的檢查,如果通過則返回,那麼初始化操作看看:

 

最後還是從definition.Methods中取出,逐個遍歷其中的方法做一個分類存儲,如果有靜態構造函數,且滿足對於的參數條件,則執行一次靜態構造。

回到開始,在獲取到類和方法的相關信息後,就可以執行對於的參數檢驗,然後執行反射:

可見,就是獲取到一個IL的解釋器,然後執行相應的反射,具體Run怎麼執行,就不繼續深入貼圖了,有興趣的可以持續跟蹤(基本思路就是對stack的操作,塞入各個參數,然後執行一次操作,塞入結果,然後退回)

對於ILRuntime的反射基本就先研究到這兒,如果要應用到自己的項目中,可以繼續深入研究一下代碼,看看實現的具體細節。這兒附上開源的相關文檔:

ILRuntime中的反射

二、熱更新DLL和Unity主工程的相互調用

基於前面的反射,我們可以基本理出熱更DLL和unity主工程的交互本質: 基於IL虛擬機或者.net本身反射來實現交互,對於熱更新DLL,其調用unity主工程,則主要是在熱更新工程中添加對於unity工程的Assembly-CSharp的引用:

基於這個引用,可以調用其中類的各自方法,舉兩個類來測試:

一個不繼承自MonoBehaviour:

一個繼承自MonoBehaviour:

這兩個Unity主工程中的類以及其中的方法,在熱更新DLL中調用:

可以在Unity主工程中得到輸出:

 

看一下track可以大概瞭解整個反射的執行過程。

對於Unity執行熱更DLL中的調用,就是第一部分的反射實例。

 

總結:絮絮叨叨的寫了三篇文章,算是對最近的研究做一個總結吧,現在項目還在評估這種熱更新方案,基於穩妥,以及有基於slua的熱更新項目用過,ILRuntime還在評估,其實本質都是相通的:基於自我實現的虛擬機(lua的虛擬機或者IL虛擬機,均基於棧實現),來構建一個自我運行環境,在裏面解析執行對於的指令(lua虛擬機的指令/IL語句),來實現對熱更新的代碼(雖然是以資源方式熱更新)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章