Go中也提供了反射機制,與Java一樣Go的反射也是在運行時獲取對象的相關信息,更新對象內部狀態;Golang通過反射可以獲取對象類型、欄位類型與值、調用struct實例方法、更新實例值等; Go關於反射相關的對象、函數都在reflect包中最主要的兩個為:Type與Value; Go提供了下麵兩個 ...
Go中也提供了反射機制,與Java一樣Go的反射也是在運行時獲取對象的相關信息,更新對象內部狀態;Golang通過反射可以獲取對象類型、欄位類型與值、調用struct實例方法、更新實例值等;
Go關於反射相關的對象、函數都在reflect包中最主要的兩個為:Type與Value;
Go提供了下麵兩個函數,這兩個是Go反射的核心;
reflect.TypeOf 返回目標對象的類型
reflect.ValueOf 返回值目標對象的值
t:=1
fmt.Println(reflect.TypeOf(t), reflect.ValueOf(t))
輸出:int 1
通過反射操作Struct
type Demo struct {
Id int
Name string
}
func (d *Demo) Back() {
fmt.Println("調用方法 Back")
}
func (d *Demo) Add(a, b int) int {
return a + b
}
獲取結構體中每個成員變數的名稱與值:
d := &Demo{Id: 2, Name: "test"}
getValue(d)
輸出: Id : 2
Name : test
func getValue(v interface{}) {
t := reflect.TypeOf(v)
o := reflect.ValueOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem() //獲取類型指針中的類型
}
if o.Kind() == reflect.Ptr {
o = o.Elem() //獲取值地址中的值
}
num := t.NumField() //獲取欄位個數
for i := 0; i < num; i++ {
f := o.Field(i) //獲取欄位的值
fieldName := t.Field(i).Name //獲取欄位名稱
switch f.Kind() {
case reflect.Int:
fmt.Println(fieldName, ": ", f.Int())
case reflect.String:
fmt.Println(fieldName, ": ", f.String())
default:
fmt.Println("類型不不支持")
}
}
}
對於引用類型使用reflect.TypeOf返回的是該類型的指針,reflect.ValueOf返回的是該類型的值地址;所以對於引用類型都要的相關操作都要調用Elem()函數獲取真實的類型與值;
調用Type或Value對象的NumField函數獲取結構體的欄位個數
調用Value對象的Field(i) 可獲取欄位的值;
調用Value對象的Kind()函數可獲取欄位的類型;
該Value對應於哪種類型調用對應的函數即可獲取得到相應的值,如類型不一致將拋出:panic: reflect: call of reflect.Value.Xxx on int Value;
修改結構體中每個成員變數的值:
d := new(Demo)
setValue(d)
fmt.Println(d)
輸出:&{88 Test}
func setValue(v interface{}) {
t := reflect.TypeOf(v)
o := reflect.ValueOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if o.Kind() == reflect.Ptr {
o = o.Elem()
}
num := t.NumField()
for i := 0; i < num; i++ {
f := o.Field(i)
switch f.Kind() {
case reflect.Int:
f.SetInt(88) //往該欄位設值
case reflect.String:
f.SetString("Test") /往該欄位設值
default:
fmt.Println("類型不支持")
}
}
}
修改欄位值與獲取值一樣,類型一定要一致,如不一致將拋異常,如int類型卻調用SetString設值:panic: reflect: call of reflect.Value.SetString on int Value;
調用結構體的無參方法:
d := new(Demo)
callMethod(d)
輸出:調用方法 Back
func callMethod(v interface{}) {
o := reflect.ValueOf(v)
o.MethodByName("Back").Call(nil)
}
調用MethodByName根據名稱獲取方法,Call調用該方法;
調用結構體的有參方法:
d := new(Demo)
callMethodParam (d)
輸出:3
func callMethodParam(p interface{}) {
o := reflect.ValueOf(p)
args:=[]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)}
v:= o.MethodByName("Add").Call(args)
fmt.Println(v[0])
}
比較常用的方法還有:
使用名字獲取結構體的成員
Refletct.ValueOf(*e).FieldByName(“Name”)
獲取結構體成員的json標記信息
reflect.TypeOf(s) . Field(0).Tag.Get(“key”)
Golang的反射也遵循Go語言規則,反射無法修改結構體中的私有對象,無法調用私有私有方法,可訪問私有成員,修改私有成員將會拋出reflect.flag.mustBeAssignable異常;
文章首發地址:Solinx
https://mp.weixin.qq.com/s/W0UVbFxMMeXA5HuNNZlK-A