Golang反射-下篇
目錄
1、判斷類型 interface.Type
2、自定義 struct 的反射
3、結(jié)構(gòu)體標(biāo)簽和反射
4、反射調(diào)用函數(shù)
5、反射調(diào)用方法
6、反射創(chuàng)建值
6.1 反射創(chuàng)建 struct
6.2 反射創(chuàng)建 slice
6.3 反射創(chuàng)建 map
7、反射修改值
7.1 反射修改 struct
7.2 反射修改 slice
7.3 反射修改 map

本文是?Golang反射-上篇 的續(xù)篇內(nèi)容,主要介紹反射實(shí)際的一些使用
1、判斷類型 interface.Type
利用類型斷言來判斷數(shù)據(jù)類型的用法如下
package?main
import?"fmt"
func?main()??{
?var?s?interface{}?=?"abc"
?switch?s.(type)?{
?case?string:
??fmt.Println("s.type=string")
?case?int:
??fmt.Println("s.type=int")
?case?bool:
??fmt.Println("s.type=bool")
?default:
??fmt.Println("未知的類型")
?}
}
上述類型判斷的問題
類型判斷會(huì)寫很多,代碼很長(zhǎng) 類型還會(huì)增刪,不靈活
如果使用反射獲取變量?jī)?nèi)部的信息
reflect 包提供 ValueOf 和 TypeOf reflect.ValueOf:獲取輸入接口中數(shù)據(jù)的值,如果為空返回 0 reflect.TypeOf:獲取輸入接口中值的類型,如果為空返回 nil TypeOf 能傳入所有類型,是因?yàn)樗械念愋投紝?shí)現(xiàn)了空接口
package?main
import?(
?"fmt"
?"reflect"
)
func?main()??{
?var?s?interface{}?=?"abc"
?//TypeOf會(huì)返回目標(biāo)的對(duì)象
?reflectType:=reflect.TypeOf(s)
?reflectValue:=reflect.ValueOf(s)
?fmt.Printf("[typeof:%v]\n",?reflectType)??//?string
?fmt.Printf("[valueof:%v]\n",?reflectValue)??//?abc
}
2、自定義 struct 的反射
自定義 struct 的相關(guān)操作
對(duì)于成員變量
先獲取 interface 的 reflect.Type,然后遍歷 NumField 再通過 reflect.Type 的 Field 獲取字段名及類型 最后通過 Field 的 interface 獲取對(duì)應(yīng)的 value 對(duì)于方法
先獲取 interface 的 reflect.Type,然后遍歷 NumMethod 再通過 reflect.Type 的 t.Method 獲取真實(shí)的方法名 最后通過 Name 和 Type 獲取方法的類型和值
注意點(diǎn)
用于對(duì)未知類型進(jìn)行遍歷探測(cè)其 Field,抽象成一個(gè)函數(shù) go 語言里面 struct 成員變量小寫,在反射的時(shí)候直接 panic() 結(jié)構(gòu)體方法名小寫是不會(huì) panic 的,反射值也不會(huì)被查看到 指針方法是不能被反射查看到的
package?main
import?(
?"fmt"
?"reflect"
)
type?Person?struct?{
?Name?string
?Age??int
}
type?Student?struct?{
?Person?????//?匿名結(jié)構(gòu)體嵌套
?StudentId??int
?SchoolName?string
?Graduated??bool
?Hobbies????[]string
?//panic:?reflect.Value.Interface:?cannot?return?value?obtained?from?unexported?field?or?method
?//hobbies????[]string
?Label??????map[string]string
}
func?(s?*Student)?GoHome()?{
?fmt.Printf("回家了,sid:%d\n",?s.StudentId)
}
//func?(s?Student)?GoHome()?{
//?fmt.Printf("回家了,sid:%d\n",?s.StudentId)
//}
func?(s?Student)?GotoSchool()?{
?fmt.Printf("上學(xué)了,sid:%d\n",?s.StudentId)
}
func?(s?*Student)?graduated()?{
?fmt.Printf("畢業(yè)了,sid:%d\n",?s.StudentId)
}
//func?(s?Student)?Ggraduated()?{
//?fmt.Printf("畢業(yè)了,sid:%d\n",?s.StudentId)
//}
func?reflectProbeStruct(s?interface{})?{
?//?獲取目標(biāo)對(duì)象
?t?:=?reflect.TypeOf(s)
?fmt.Printf("對(duì)象的類型名稱?%s\n",?t.Name())
?//?獲取目標(biāo)對(duì)象的值類型
?v?:=?reflect.ValueOf(s)
?//?遍歷獲取成員變量
?for?i?:=?0;?i???//?Field?代表對(duì)象的字段名
??key?:=?t.Field(i)
??value?:=?v.Field(i).Interface()
??//?字段
??if?key.Anonymous?{
???fmt.Printf("匿名字段?第?%d?個(gè)字段,字段名?%s,?字段類型?%v,?字段的值?%v\n",?i+1,?key.Name,?key.Type,?value)
??}?else?{
???fmt.Printf("命名字段?第?%d?個(gè)字段,字段名?%s,?字段類型?%v,?字段的值?%v\n",?i+1,?key.Name,?key.Type,?value)
??}
?}
?//?打印方法
?for?i?:=?0;?i???m?:=?t.Method(i)
??fmt.Printf("第?%d?個(gè)方法,方法名?%s,?方法類型?%v\n",?i+1,?m.Name,?m.Type)
?}
}
func?main()?{
?s?:=?Student{
??Person:?Person{
???"geek",
???24,
??},
??StudentId:??123,
??SchoolName:?"Beijing?University",
??Graduated:??true,
??Hobbies:????[]string{"唱",?"跳",?"Rap"},
??//hobbies:????[]string{"唱",?"跳",?"Rap"},
??Label:??????map[string]string{"k1":?"v1",?"k2":?"v2"},
?}
?p?:=?Person{
??Name:?"張三",
??Age:??100,
?}
?reflectProbeStruct(s)
?reflectProbeStruct(p)
?/*
?對(duì)象的類型名稱?Student
?匿名字段?第?1?個(gè)字段,字段名?Person,?字段類型?main.Person,?字段的值?{geek?24}
?命名字段?第?2?個(gè)字段,字段名?StudentId,?字段類型?int,?字段的值?123
?命名字段?第?3?個(gè)字段,字段名?SchoolName,?字段類型?string,?字段的值?Beijing?University
?命名字段?第?4?個(gè)字段,字段名?Graduated,?字段類型?bool,?字段的值?true
?命名字段?第?5?個(gè)字段,字段名?Hobbies,?字段類型?[]string,?字段的值?[唱?跳?Rap]
?命名字段?第?6?個(gè)字段,字段名?Label,?字段類型?map[string]string,?字段的值?map[k1:v1?k2:v2]
?第?1?個(gè)方法,方法名?GotoSchool,?方法類型?func(main.Student)
?對(duì)象的類型名稱?Person
?命名字段?第?1?個(gè)字段,字段名?Name,?字段類型?string,?字段的值?張三
?命名字段?第?2?個(gè)字段,字段名?Age,?字段類型?int,?字段的值?100
??*/
}
3、結(jié)構(gòu)體標(biāo)簽和反射
json 的標(biāo)簽解析出 json yaml 的標(biāo)簽解析出 yaml xorm、gorm 的標(biāo)簽標(biāo)識(shí)數(shù)據(jù)庫(kù) db 字段 自定義標(biāo)簽 原理是 t.Field.Tag.Lookup("標(biāo)簽名")
示例
package?main
import?(
?"encoding/json"
?"fmt"
?"gopkg.in/yaml.v2"
?"io/ioutil"
)
type?Person?struct?{
?Name?string?`json:"name"?yaml:"yaml_name"`
?Age??int????`json:"age"?yaml:"yaml_age"`
?City?string?`json:"city"?yaml:"yaml_city"`
?//City?string?`json:"-"?yaml:"yaml_city"`?//?忽略json:"-"
}
//?json解析
func?jsonWork()?{
?//?對(duì)象Marshal成字符串
?p?:=?Person{
??Name:?"geek",
??Age:??24,
??City:?"Beijing",
?}
?data,?err?:=?json.Marshal(p)
?if?err?!=?nil?{
??fmt.Printf("json.marshal.err:?%v\n",?err)
?}
?fmt.Printf("person.marshal.res:?%v\n",?string(data))
?//?從字符串解析成結(jié)構(gòu)體
?p2str?:=?`{
?"name":?"張三",
?"age":?38,
?"city":?"山東"
?}`
?var?p2?Person
?err?=?json.Unmarshal([]byte(p2str),?&p2)
?if?err?!=?nil?{
??fmt.Printf("json.unmarshal.err:?%v\n",?err)
??return
?}
?fmt.Printf("person.unmarshal.res:?%v\n",?p2)
}
//?yaml解析
func?yamlWork()?{
?filename?:=?"a.yaml"
?content,?err?:=?ioutil.ReadFile(filename)
?if?err?!=?nil?{
??fmt.Printf("ioutil.ReadFile.err:?%v\n",?err)
??return
?}
?p?:=?&Person{}
?//err?=?yaml.Unmarshal([]byte(content),?p)
?err?=?yaml.UnmarshalStrict([]byte(content),?p)??//?解析嚴(yán)格,考慮多余字段,忽略字段等
?if?err?!=?nil?{
??fmt.Printf("yaml.UnmarshalStrict.err:?%v\n",?err)
??return
?}
?fmt.Printf("yaml.UnmarshalStrict.res:?%v\n",?p)
}
func?main()?{
?jsonWork()
?/*
??person.marshal.res:?{"name":"geek","age":24,"city":"Beijing"}
??person.unmarshal.res:?{張三?38?山東}
?*/
?yamlWork()
?/*
??yaml.UnmarshalStrict.res:?&{李四?18?Shanghai}
??*/
}
解析的 yaml 內(nèi)容
yaml_name:?李四
yaml_age:?18
yaml_city:?Shanghai
自定義標(biāo)簽格式解析
package?main
import?(
?"fmt"
?"reflect"
)
type?Person?struct?{
?Name?string?`aa:"name"`
?Age??int????`aa:"age"`
?City?string?`aa:"city"`
}
//?CustomParse?自定義解析
func?CustomParse(s?interface{})?{
?//?TypeOf?type類型
?r:=reflect.TypeOf(s)
?value?:=?reflect.ValueOf(s)
?for?i:=0;i??field:=r.Field(i)
??key:=field.Name
??if?tag,?ok:=field.Tag.Lookup("aa");ok{
???if?tag?==?"-"{
????continue
???}
???fmt.Printf("找到了aa標(biāo)簽,?key:?%v,?value:?%v,?tag:?%s\n",?key,?value.Field(i),?tag)
??}
?}
}
func?main()?{
?p?:=?Person{
??Name:?"geek",
??Age:??24,
??City:?"Beijing",
?}
?CustomParse(p)
?/*
?找到了aa標(biāo)簽,?key:?Name,?value:?geek,?tag:?name
?找到了aa標(biāo)簽,?key:?Age,?value:?24,?tag:?age
?找到了aa標(biāo)簽,?key:?City,?value:?Beijing,?tag:?city
??*/
}
4、反射調(diào)用函數(shù)
valueFunc?:=?reflect.ValueOf(Add)?//函數(shù)也是一種數(shù)據(jù)類型
typeFunc?:=?reflect.TypeOf(Add)
argNum?:=?typeFunc.NumIn()????????????//函數(shù)輸入?yún)?shù)的個(gè)數(shù)
args?:=?make([]reflect.Value,?argNum)?//準(zhǔn)備函數(shù)的輸入?yún)?shù)
for?i?:=?0;?i??if?typeFunc.In(i).Kind()?==?reflect.Int?{
??args[i]?=?reflect.ValueOf(3)?//給每一個(gè)參數(shù)都賦3
?}
}
sumValue?:=?valueFunc.Call(args)?//返回[]reflect.Value,因?yàn)間o語言的函數(shù)返回可能是一個(gè)列表
if?typeFunc.Out(0).Kind()?==?reflect.Int?{
?sum?:=?sumValue[0].Interface().(int)?//從Value轉(zhuǎn)為原始數(shù)據(jù)類型
?fmt.Printf("sum=%d\n",?sum)
}
5、反射調(diào)用方法
示例
user?:=?User{
?Id:?????7,
?Name:???"杰克遜",
?Weight:?65.5,
?Height:?1.68,
}
valueUser?:=?reflect.ValueOf(&user)??????????????//必須傳指針,因?yàn)锽MI()在定義的時(shí)候它是指針的方法
bmiMethod?:=?valueUser.MethodByName("BMI")???????//MethodByName()通過Name返回類的成員變量
resultValue?:=?bmiMethod.Call([]reflect.Value{})?//無參數(shù)時(shí)傳一個(gè)空的切片
result?:=?resultValue[0].Interface().(float32)
fmt.Printf("bmi=%.2f\n",?result)
//Think()在定義的時(shí)候用的不是指針,valueUser可以用指針也可以不用指針
thinkMethod?:=?valueUser.MethodByName("Think")
thinkMethod.Call([]reflect.Value{})
valueUser2?:=?reflect.ValueOf(user)
thinkMethod?=?valueUser2.MethodByName("Think")
thinkMethod.Call([]reflect.Value{})
過程
首先通過 reflect.ValueOf(p1) 獲取得到反射類型對(duì)象 reflect.ValueOf(p1).MethodByName 需 要傳入準(zhǔn)確的方法名稱(名稱不對(duì)會(huì) panic: reflect: call of reflect.Value.Call on zero Value),MethodByName 代表注冊(cè) []reflect.Value 這是最終需要調(diào)用方法的參數(shù),無參數(shù)傳空切片 call 調(diào)用
package?main
import?(
?"fmt"
?"reflect"
)
type?Person?struct?{
?Name???string
?Age????int
?Gender?string
}
func?(p?Person)?ReflectCallFuncWithArgs(name?string,?age?int)?{
?fmt.Printf("調(diào)用的是帶參數(shù)的方法,?args.name:?%s,?args.age:?%d,?p.name:?%s,?p.age:?%d\n",
??name,
??age,
??p.Name,
??p.Age,
?)
}
func?(p?Person)?ReflectCallFuncWithNoArgs()?{
?fmt.Printf("調(diào)用的是不帶參數(shù)的方法\n")
}
func?main()?{
?p1?:=?Person{
??Name:???"geek",
??Age:????24,
??Gender:?"男",
?}
?//?1.首先通過reflect.ValueOf(p1)獲取得到反射值類型
?getValue?:=?reflect.ValueOf(p1)
?//?2.帶參數(shù)的方法調(diào)用
?methodValue1?:=?getValue.MethodByName("ReflectCallFuncWithArgs")
?//?參數(shù)是reflect.Value的切片
?args1?:=?[]reflect.Value{reflect.ValueOf("張三"),?reflect.ValueOf(30)}
?methodValue1.Call(args1)
?//?3.不帶參數(shù)的方法調(diào)用
?methodValue2?:=?getValue.MethodByName("ReflectCallFuncWithNoArgs")
?//?參數(shù)是reflect.Value的切片
?args2?:=?make([]reflect.Value,?0)
?methodValue2.Call(args2)
?/*
?調(diào)用的是帶參數(shù)的方法,?args.name:?張三,?args.age:?30,?p.name:?geek,?p.age:?24
?調(diào)用的是不帶參數(shù)的方法
??*/
}
6、反射創(chuàng)建值
6.1 反射創(chuàng)建 struct
t?:=?reflect.TypeOf(User{})
value?:=?reflect.New(t)?//根據(jù)reflect.Type創(chuàng)建一個(gè)對(duì)象,得到該對(duì)象的指針,再根據(jù)指針提到reflect.Value
value.Elem().FieldByName("Id").SetInt(10)
value.Elem().FieldByName("Name").SetString("宋江")
value.Elem().FieldByName("Weight").SetFloat(78.)
value.Elem().FieldByName("Height").SetFloat(168.4)
user?:=?value.Interface().(*User)?//把反射類型轉(zhuǎn)成go原始數(shù)據(jù)類型
fmt.Printf("id=%d?name=%s?weight=%.1f?height=%.1f\n",?user.Id,?user.Name,?user.Weight,?user.Height)
6.2 反射創(chuàng)建 slice
var?slice?[]User
sliceType?:=?reflect.TypeOf(slice)
sliceValue?:=?reflect.MakeSlice(sliceType,?1,?3)?//reflect.MakeMap、reflect.MakeSlice、reflect.MakeChan、reflect.MakeFunc
sliceValue.Index(0).Set(reflect.ValueOf(User{
?Id:?????8,
?Name:???"李達(dá)",
?Weight:?80,
?Height:?180,
}))
users?:=?sliceValue.Interface().([]User)
fmt.Printf("1st?user?name?%s\n",?users[0].Name)
6.3 反射創(chuàng)建 map
var?userMap?map[int]*User
mapType?:=?reflect.TypeOf(userMap)
//?mapValue:=reflect.MakeMap(mapType)
mapValue?:=?reflect.MakeMapWithSize(mapType,?10)?//reflect.MakeMap、reflect.MakeSlice、reflect.MakeChan、reflect.MakeFunc
user?:=?&common.User{
?Id:?????7,
?Name:???"杰克遜",
?Weight:?65.5,
?Height:?1.68,
}
key?:=?reflect.ValueOf(user.Id)
mapValue.SetMapIndex(key,?reflect.ValueOf(user))????????????????????//SetMapIndex?往map里添加一個(gè)key-value對(duì)
mapValue.MapIndex(key).Elem().FieldByName("Name").SetString("令狐一刀")?//MapIndex?根據(jù)Key取出對(duì)應(yīng)的map
userMap?=?mapValue.Interface().(map[int]*User)
fmt.Printf("user?name?%s?%s\n",?userMap[7].Name,?user.Name)
7、反射修改值
反射修改值要求必須是指針類型
修改值的操作:pointer.Elem().Setxxx()
package?main
import?(
?"fmt"
?"reflect"
)
func?main()?{
?var?num?float64?=?3.14
?fmt.Printf("原始值?%f\n",?num)
?//?通過reflect.ValueOf獲取num中的value,必須是指針才可以修改值
?//pointer?:=?reflect.ValueOf(num)??//?直接傳值會(huì)panic
?pointer?:=?reflect.ValueOf(&num)
?newValue?:=?pointer.Elem()
?//?賦新的值
?newValue.SetFloat(5.66)
?fmt.Printf("新的值?%f\n",?num)
}
7.1 反射修改 struct
user?:=?User{
?Id:?????7,
?Name:???"杰克遜",
?Weight:?65.5,
?Height:?1.68,
}
valueUser?:=?reflect.ValueOf(&user)
//?valueS.Elem().SetInt(8)//會(huì)panic
valueUser.Elem().FieldByName("Weight").SetFloat(68.0)?//FieldByName()通過Name返回類的成員變量。不能在指針Value上調(diào)用FieldByName
addrValue?:=?valueUser.Elem().FieldByName("addr")
if?addrValue.CanSet()?{
?addrValue.SetString("北京")
}?else?{
?fmt.Println("addr是未導(dǎo)出成員,不可Set")?//以小寫字母開頭的成員相當(dāng)于是私有成員
}
7.2 反射修改 slice
下面示例,間接的實(shí)現(xiàn)了 append 功能
users?:=?make([]*User,?1,?5)?//len=1,cap=5
sliceValue?:=?reflect.ValueOf(&users)?//準(zhǔn)備通過Value修改users,所以傳users的地址
if?sliceValue.Elem().Len()?>?0?{??????//取得slice的長(zhǎng)度
?sliceValue.Elem().Index(0).Elem().FieldByName("Name").SetString("哈哈哈")
?//?u0?:=?users[0]
?fmt.Printf("1st?user?name?change?to?%s\n",?users[0].Name)
}
sliceValue.Elem().SetCap(3)?//新的cap必須位于原始的len到cap之間
sliceValue.Elem().SetLen(2)
//調(diào)用reflect.Value的Set()函數(shù)修改其底層指向的原始數(shù)據(jù)
sliceValue.Elem().Index(1).Set(reflect.ValueOf(&User{
?Id:?????8,
?Name:???"geek",
?Weight:?80,
?Height:?180,
}))
fmt.Printf("2nd?user?name?%s\n",?users[1].Name)
7.3 反射修改 map
u1?:=?&User{
?Id:?????7,
?Name:???"杰克遜",
?Weight:?65.5,
?Height:?1.68,
}
u2?:=?&User{
?Id:?????8,
?Name:???"杰克遜",
?Weight:?65.5,
?Height:?1.68,
}
userMap?:=?make(map[int]*User,?5)
userMap[u1.Id]?=?u1
mapValue?:=?reflect.ValueOf(&userMap)?????????????????????????????????????????????????????????//準(zhǔn)備通過Value修改userMap,所以傳userMap的地址
mapValue.Elem().SetMapIndex(reflect.ValueOf(u2.Id),?reflect.ValueOf(u2))??????????????????????//SetMapIndex?往map里添加一個(gè)key-value對(duì)
mapValue.Elem().MapIndex(reflect.ValueOf(u1.Id)).Elem().FieldByName("Name").SetString("令狐一刀")?//MapIndex?根據(jù)Key取出對(duì)應(yīng)的map
for?k,?user?:=?range?userMap?{
?fmt.Printf("key?%d?name?%s\n",?k,?user.Name)
}
See you ~
歡迎進(jìn)群一起進(jìn)行技術(shù)交流
加群方式:公眾號(hào)消息私信“加群”或加我好友再加群均可
評(píng)論
圖片
表情
