1. TYPE
-
反射讓我們能在執行初期探知物件的型別資訊和記憶體結構,這從一定程度上彌補了靜態語言在動態行為上的不足。同時,反射還是實現超程式設計的重要手段
-
-
在面對型別時,需要區分 TYPE 和 KIND ,前者表示真是型別(靜態型別),後者表示其基礎機構(底層型別)類別
-
除了透過實際物件獲取型別外,也可直接構造一些基礎符合型別
-
傳入物件應區分基型別和指標型別,因為它們並不屬於同一型別
-
方法 Elem 返回指標、陣列、切片、字典 (值)或通道的基型別
-
只有在獲取結構體指標的基型別之後,才能遍歷它的欄位
-
對於匿名欄位,可用多級索引 (按定義順序)直接訪問
-
FieldByName 不支援多級名稱,如有同名遮蔽,須透過匿名欄位二次獲取
-
同樣地,輸出方法集時,一樣區分基型別和指標型別
-
有一點和想象的不同,反射能探知當前包或外包的非匯出結構成員 (相對 reflect 而言,當前包和外包都是 “ 外包 ”)
-
可用反射提取 struct tag,還能自動分解。其常用於 ORM 對映,或資料格式驗證
-
輔助判斷方法 Implements、Convertible、AssignableTo 都是執行期進行動態呼叫和賦值所必須的。
2. Value
-
和 TYPE 獲取型別資訊不同,Value 專注於物件例項資料讀寫
-
介面變數會複製物件,且是 Unaddressable 的,所以想要修改目標物件,就必須要使用指標
-
但是就算傳入指標,一樣需要透過 Elem 獲取目標物件。因為被介面儲存的指標本身是不能定址和進行設定操作的
Attention:
不能對非匯出欄位直接進行設定操作,無論是當前包還是外包
-
Value.Pointer 和 Value.Int 等方法類似,將 Value.data 儲存的資料轉換為指標,目標必須是指標型別。而 UnsafeAddr 返回任何 CanAddr Value.data 地址 (相當於 & 取地址操作),比如 Elem 後的 Value,以及欄位成員地址
-
以結構體裡的指標型別欄位為例,Pointer 返回該欄位所儲存的地址,而 UnsafeAddr 返回該欄位自身的地址 (結構物件地址 + 偏移量)
-
可透過 Interface 方法進行型別推斷和轉換
-
也可以直接使用 Value.Int、Bool 等方法進行型別轉換,但失敗時會引發 Panic,且不支援 ok-idom
-
介面有兩種 nil 狀態,這是一個潛在的麻煩。解決的辦法是用 IsNil 判斷值是否為 nil
-
也可用 unsafe 轉換後直接判斷 iface.data 是否為零值
-
讓人無奈的是,Value 裡的某些方法 並未實現 ok-idom 或返回 error,所以得自行判斷返回的是否為 Zero Value
3. Method
-
動態呼叫方法,談不上有多麻煩,只需要按 In 列表準備好 所需引數即可
-
對於變參來說,用 CallSlice 要更方便一些
-
無法呼叫非匯出方法,甚至無法獲取有效地址
4. 構建
-
反射庫提供了內建函式 make 和 new 的對應操作,其中最有意思的就是 MakeFunc。可以用它實現通用模板,適應不同型別資料
-
golang 暫不支援泛型,所以會麻煩一點
5.效能
-
反射在帶來“ 方便 ” 的同時,也造成了很大的困擾,很多人對反射避之不及,因為它會造成很大的效能損失。
-
如果對效能要求較高,那麼須謹慎使用反射