Goalng下的反射模塊reflect學習使用

反射reflection

反射

 ●  可以大大提高程序的靈活性,使得interface{}有更大的發揮餘地
 ●  反射使用TypeOf和ValueOf函數從接口中獲取目標對象信息
 ●  反射會將匿名字段作爲獨立字段(匿名字段的本質)
 ●  想要利用反射修改對象狀態,前提是interface.data是settable,即pointer-interface
 ●  通過反射可以"動態" 調用方法

常用的類型、函數和方法


1//返回動態類型i的類型,如果i是一個空結構體類型,TypeOf將返回nil
2func TypeOf(i interface{}) Type
3
4//Type 接口類型
5type Type interface {
6 Align() int
7 FieldAlign() int
8 //指定結構體中方法的下標,返回某個方法的對象,需要注意的是返回的Method是一個獨立的結構體
9 Method(int) Method
10 /*
11 type Method struct {
12 Name string
13 PkgPath string
14 Type Type
15 Func Value
16 Index int
17 }
18 */
19
20
21 MethodByName(string) (Method, bool)
22
23 //返回該結構體類型的方法下標
24 NumMethod() int
25 //返回類型的名稱,即動態類型i的名稱
26 Name() string
27 PkgPath() string
28 Size() uintptr
29 String() string
30 Kind() Kind
31 Implements(u Type) bool
32 AssignableTo(u Type) bool
33 ConvertibleTo(u Type) bool
34 Comparable() bool
35 Bits() int
36 ChanDir() ChanDir
37 IsVariadic() bool
38 Elem() Type
39 //返回結構體類型第i個字段
40 Field(i int) StructField
41 //StructField結構體
42 //type StructField struct {
43 // Name string
44 // PkgPath string
45 // Type Type
46 // Tag StructTag
47 // Offset uintptr
48 // Index []int
49 // Anonymous bool
50
51 //根據結構體字段索引獲取嵌入字段的結構體信息
52 FieldByIndex(index []int) StructField
53
54 FieldByName(name string) (StructField, bool)
55 FieldByNameFunc(match func(string) bool) (StructField, bool)
56 In(i int) Type
57 Key() Type
58 Len() int
59 //返回動態類型i(結構體字段)的字段總數
60 NumField() int
61 NumIn() int
62 NumOut() int
63 Out(i int) Type
64}
65
66//返回接口i的一個初始化的新值.ValueOf(nil)返回一個零值
67func ValueOf(i interface{}) Value
68
69// Value結構體
70type Value struct {
71
72}
73// Value結構體的一些方法
74// 返回結構體v中的第i個字段。如果v的類型不是結構體或者i超出了結構體的範圍,則會出現panic
75func (v Value) Field(i int) Value
76
77//以接口類型返回v的當前值
78func (v Value) Interface() (i interface{})
79//等價於.
80var i interface{} = (v's underlying value)
81
82
83//通過反射方式修改結構體對象的一些方法
84
85//返回接口v包含或者指針v包含的值
86func (v Value) Elem() Value
87//判斷該接口v是否可以被set修改
88func (v Value) CanSet() bool
89
90//使用另外一個反射接口去修改反射值
91func (v Value) Set(x Value)
92//其他不同類型的Set
93func (v Value) SetBool(x bool)
94func (v Value) SetBytes(x []byte)
95func (v Value) SetFloat(x float64)
96func (v Value) SetInt(x int64)
97//設置結構體對象v的長度爲n
98func (v Value) SetLen(n int)
99func (v Value) SetString(x string)
100
101
102//一些輔助方法
103//返回反射結構體的Value的類型.如果v爲零值,IsValid將返回false
104func (v Value) Kind() Kind
105//判斷value是否爲有效值,通常用在判斷某個字段是否在反射體的Value中
106func (v Value) IsValid() bool
107
108//Kind常量
109type Kind uint
110const (
111 Invalid Kind = iota
112 Bool
113 Int
114 Int8
115 Int16
116 Int32
117 Int64
118 Uint
119 Uint8
120 Uint16
121 Uint32
122 Uint64
123 Uintptr
124 Float32
125 Float64
126 Complex64
127 Complex128
128 Array
129 Chan
130 Func
131 Interface
132 Map
133 Ptr
134 Slice
135 String
136 Struct
137 UnsafePointer
138)
139

反射的基本操作

通過反射來獲取結構體字段的名稱以及其他相關信息。


1$ cat test-reflect.go
2package main
3import (
4 "fmt"
5 "reflect"
6)
7
8//定義結構體
9type User struct{
10 Id int
11 Name string
12 Age int
13 }
14//定義結構體方法
15func (u User) Hello(){
16 fmt.Println("Hello xuxuebiao")
17 }
18
19func main() {
20 u := User{1,"bgops",25}
21 Info(u)
22 u.Hello()
23}
24
25//定義一個反射函數,參數爲任意類型
26func Info(o interface{}) {
27 //使用反射類型獲取o的Type,一個包含多個方法的interface
28 t := reflect.TypeOf(o)
29 //打印類型o的名稱
30 fmt.Println("type:",t.Name())
31
32 //使用反射類型獲取o的Value,一個空的結構體
33 v := reflect.ValueOf(o)
34 fmt.Println("Fields:")
35
36 //t.NumField()打印結構體o的字段個數(Id,Name,Age共三個)
37 for i :=0;i< t.NumField();i++{
38 //根據結構體的下標i來獲取結構體某個字段,並返回一個新的結構體
39 /**
40 type StructField struct {
41 Name string
42 PkgPath string
43 Type Type
44 Tag StructTag
45 Offset uintptr
46 Index []int
47 Anonymous bool
48 }
49 **/
50 f := t.Field(i)
51
52 //使用結構體方法v.Field(i)根據下標i獲取字段Value(Id,Name,Age)
53 //在根據Value的Interface()方法獲取當前的value的值(interface類型)
54 val := v.Field(i).Interface()
55 fmt.Printf("%6s:%v = %v\n",f.Name,f.Type,val)
56 }
57
58 //使用t.NumMethod()獲取所有結構體類型的方法個數(只有Hello()一個方法)
59 //接口Type的方法NumMethod() int
60 for i := 0;i < t.NumMethod();i++ {
61 //使用t.Method(i)指定方法下標獲取方法對象。返回一個Method結構體
62 //Method(int) Method
63 m := t.Method(i)
64 //打印Method結構體的相關屬性
65 /*
66 type Method struct {
67 Name string
68 PkgPath string
69 Type Type
70 Func Value
71 Index int
72 }
73 */
74 fmt.Printf("%6s:%v\n",m.Name,m.Type)
75 }
76 }
77
78$ go run test-reflect.go
79type: User
80Fields:
81 Id:int = 1
82 Name:string = bgops
83 Age:int = 25
84 Hello:func(main.User)
85Hello xuxuebiao
86

注意:我們上面的示例是使用值類型進行進行反射構造的。如果是指針類型的話,我們需要使用reflect.Struct字段進行判斷接口類型的Kind()方法


1if k := t.Kind();k != reflect.Struct {
2 fmt.Println("非值類型的反射")
3 return
4}

匿名字段的反射以及嵌入字段

注意:反射會將匿名字段當做獨立的字段去處理,需要通過類型索引方式使用FieldByIndex方法去逐個判斷


1//根據指定索引返回對應的嵌套字段
2FieldByIndex(index []int) StructField
3
4type StructField struct {
5 Name string
6 PkgPath string
7 Type Type
8 Tag StructTag
9 Offset uintptr
10 Index []int
11 Anonymous bool //是否爲匿名字段
12}

示例:


1$ cat anonymous-reflect.go
2package main
3import (
4 "fmt"
5 "reflect"
6)
7
8type User struct {
9 Id int
10 Name string
11 Age int
12}
13
14type Manager struct {
15 User
16 title string
17}
18
19func main() {
20 //注意匿名字段的初始化操作
21 m := Manager{User: User{1,"biaoge",24},title:"hello biao"}
22 t := reflect.TypeOf(m)
23
24 fmt.Printf("%#v\n",t.FieldByIndex([]int{0}))
25 fmt.Printf("%#v\n",t.FieldByIndex([]int{1}))
26 fmt.Printf("%#v\n",t.FieldByIndex([]int{0,0}))
27 fmt.Printf("%#v\n",t.FieldByIndex([]int{0,1}))
28
29}
30
31$ go run anonymous-reflect.go
32reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x97260), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}
33reflect.StructField{Name:"title", PkgPath:"main", Type:(*reflect.rtype)(0x8bda0), Tag:"", Offset:0x20, Index:[]int{1}, Anonymous:false}
34reflect.StructField{Name:"Id", PkgPath:"", Type:(*reflect.rtype)(0x8b8a0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false}
35reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x8bda0), Tag:"", Offset:0x8, Index:[]int{1}, Anonymous:false}

通過反射修改目標對象

通過反射的方式去修改對象的某個值。需要注意的亮點是,首先,需要找到對象相關的名稱,其次需要找到合適的方法去修改相應的值。


1$ cat mod-value-reflect.go
2package main
3import (
4 "fmt"
5 "reflect"
6)
7
8func main() {
9 x := 123
10 v := reflect.ValueOf(&x)
11 v.Elem().SetInt(5256)
12 fmt.Println(x)
13}
14
15$ go run mod-value-reflect.go
165256

修改I的時候需要找到對象字段的名稱;並且判斷類型,使用相對正確的類型修改值


1v.FieldByName("Name");f.Kind() == reflect.String {
2 f.SetString("test string")
3}

判斷是否找到正確的字段名稱:


1f := v.FieldByName("Name1")
2//判斷反射對象Value中是否找到Name1字段
3if !f.IsValid() {
4 fmt.Println("bad field")
5 return
6}

示例:


1$ cat mod-value-reflect.go
2package main
3import (
4 "fmt"
5 "reflect"
6)
7
8type User struct {
9 Id int
10 Name string
11 Age int
12}
13
14//使用反射方式對結構體對象的修改有兩個條件
15//1.通過指針
16//2.必須是可set的方法
17func main() {
18 num := 123
19 numv := reflect.ValueOf(&num)
20 //通過Value的Elem()和SetX()方法可直接對相關的對象進行修改
21 numv.Elem().SetInt(666)
22 fmt.Println(num)
23
24 u := User{1,"biao",24}
25 uu := reflect.ValueOf(&u)
26 //Set()後面的必須是值類型
27 //func (v Value) Set(x Value)
28 test := User{2,"bgops",2}
29 testv := reflect.ValueOf(test)
30 uu.Elem().Set(testv)
31 fmt.Println("Change the test to u with Set(x Value)",uu)
32
33 //此時的U已經被上面那個uu通過指針的方式修改了
34 Set(&u)
35 fmt.Println(u)
36}
37
38func Set(o interface{}) {
39 v := reflect.ValueOf(o)
40 //判斷反射體值v是否是Ptr類型並且不能進行Set操作
41 if v.Kind() == reflect.Ptr && ! v.Elem().CanSet() {
42 fmt.Println("xxx")
43 return
44 //初始化對象修改後的返回值(可接受v或v的指針)
45 } else {
46 v = v.Elem()
47 }
48 //按照結構體對象的名稱進行查找filed,並判斷類型是否爲string,然後進行Set
49 if f := v.FieldByName("Name"); f.Kind() == reflect.String {
50 f.SetString("BYBY")
51 }
52 }
53
54$ go run mod-value-reflect.go
55666
56Change the test to u with Set(x Value) &{2 bgops 2}
57{2 BYBY 2}

通過反射進行動態方法的調用

使用反射的相關知識進行方法的動態調用


1$ cat method-reflect.go
2package main
3import (
4 "fmt"
5 "reflect"
6)
7
8type User struct {
9 Id int
10 Name string
11 Age int
12}
13
14func (u User) Hello(name string,id int) {
15 fmt.Printf("Hello %s,my name is %s and my id is %d\n",name,u.Name,id)
16}
17
18func main() {
19 u := User{1,"biaoge",24}
20 fmt.Println("方法調用:")
21 u.Hello("xuxuebiao",121)
22
23 //獲取結構體類型u的Value
24 v := reflect.ValueOf(u)
25 //根據方法名稱獲取Value中的方法對象
26 mv := v.MethodByName("Hello")
27
28 //構造一個[]Value類型的變量,使用Value的Call(in []Value)方法進行動態調用method
29 //這裏其實相當於有一個Value類型的Slice,僅一個字段
30 args := []reflect.Value{reflect.ValueOf("xuxuebiao"),reflect.ValueOf(5256)}
31 fmt.Println("通過反射動態調用方法:")
32 //使用Value的Call(in []Value)方法進行方法的動態調用
33 //func (v Value) Call(in []Value) []Value
34 //需要注意的是當v的類型不是Func的化,將會panic;同時每個輸入的參數args都必須對應到Hello()方法中的每一個形參上
35 mv.Call(args)
36
37}
38
39$ go run method-reflect.go
40方法調用:
41Hello xuxuebiao,my name is biaoge and my id is 121
42通過反射動態調用方法:
43Hello xuxuebiao,my name is biaoge and my id is 5256

原文發佈時間爲:2018-11-13
本文作者:BGbiao
本文來自雲棲社區合作伙伴“Golang語言社區”,瞭解相關信息可以關注“Golang語言社區”。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章