Go反射機制Reflect
參考文章:
ofollow,noindex">Go語言實戰筆記(二十四)| Go 反射 (推薦去看)
Go語言實戰筆記(二十五)| Go Struct Tag (推薦去看)
Go語言中反射包的實現原理(The Laws of Reflection)
和Java語言一樣,Go也實現執行時反射,這為我們提供一種可以在執行時操作任意型別物件的能力。Go是靜態型別化的。每個變數都有一個靜態型別,也就是說,在編譯的時候變數的型別就被很精確地確定下來了,比如要麼是int,或者是float32,或者是MyType型別,或者是[]byte等等。
1、第一反射定律(介面值到反射物件的反射)TypeOf和ValueOf
在Go的反射定義中,任何介面都會由兩部分組成的,一個是介面的具體型別,一個是具體型別對應的值。比如 var i int = 3
,因為 interface{}
可以表示任何型別,所以變數 i
可以轉為 interface{}
,所以可以把變數 i
當成一個介面,那麼這個變數在Go反射中的表示就是 <Value,Type>
,其中Value為變數的值 3
,Type變數的為型別 int
。
reflect.Typeof獲取具體的型別;reflect.Valueof獲取介面的value。
type Myint int type User struct { name string age int } func main() { u := User{name:"lyd",age:24} var num Myint = 18 t := reflect.TypeOf(u)//main.User v := reflect.ValueOf(u)//{lyd 24} t_num := reflect.TypeOf(num)//main.Myint v_num := reflect.ValueOf(num) //18 } //還可以這樣列印 fmt.Printf("%T\n",u) fmt.Printf("%v\n",u) v.Type() //main.User複製程式碼
2、將reflect.Value轉原始型別(第二反射定律)
上面的例子我們可以通過 reflect.ValueOf
函式把任意型別的物件轉為一個 reflect.Value
,給定一個reflect.Value,我們能用Interface方法把它恢復成一個介面值;
type Myint int type User struct { name string age int } func main() { u := User{name:"lyd",age:24} var num Myint = 18 v := reflect.ValueOf(u) v_num := reflect.ValueOf(num) y := v.Interface().(User) //fmt.Println可以處理interface{}, 所以可以直接 y_num := v_num.Interface() fmt.Println(y,y_num) //{lyd 24} 18 } 複製程式碼
3、修改反射物件的值(第三反射定律)
reflect.ValueOf
函式返回的是一份值的拷貝,所以前提是我們是傳入要修改變數的地址。 其次需要我們呼叫 Elem
方法找到這個指標指向的值。
Value
為我們提供了 CanSet
方法可以幫助我們判斷Value的settablity
一:反射物件不是settable的
func main() { var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) // Error: will panic. } 複製程式碼
func main() { var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("settability of v:", v.CanSet())//settability of v: false } 複製程式碼
var x float64 = 3.4 p := reflect.ValueOf(&x) // Note: take the address of x.注意這裡哦!我們把x地址傳進去了! fmt.Println("type of p:", p.Type())// type of p: *float64 fmt.Println("settability of p:", p.CanSet())//settability of p: false 複製程式碼
反射物件p不是settable的,但是我們想要設定的不是p,而是(效果上來說)*p。為了得到p指向的東西,我們呼叫Value的Elem方法
二:總之,下面這樣才是可以改變值的
func main() { x:=2 v:=reflect.ValueOf(&x) v.Elem().SetInt(100) fmt.Println(x) }複製程式碼
4、獲取底層型別
type User struct { name string age int } func main() { u := User{name:"lyd",age:24} t := reflect.TypeOf(u) v := reflect.ValueOf(u) fmt.Println(t.Kind(),v.Kind()) //struct struct } 複製程式碼
5、遍歷欄位和方法
通過反射,我們可以獲取一個結構體型別的欄位,也可以獲取一個型別的匯出方法
type User struct { name string age int } func main() { u := User{name:"lyd",age:24} t := reflect.TypeOf(u) for i:=0;i<t.NumField();i++ { fmt.Println(t.Field(i).Name) } } //name //age複製程式碼
//方法 for i:=0;i<t.NumMethod() ;i++{ fmt.Println(t.Method(i).Name) }複製程式碼
6、動態呼叫方法