Golang反射-下篇
目錄
1、判斷類型 interface.Type
2、自定義 struct 的反射
3、結構體標簽和反射
4、反射調用函數
5、反射調用方法
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ù)篇內容,主要介紹反射實際的一些使用
1、判斷類型 interface.Type
利用類型斷言來判斷數據類型的用法如下
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("未知的類型")
?}
}
上述類型判斷的問題
類型判斷會寫很多,代碼很長 類型還會增刪,不靈活
如果使用反射獲取變量內部的信息
reflect 包提供 ValueOf 和 TypeOf reflect.ValueOf:獲取輸入接口中數據的值,如果為空返回 0 reflect.TypeOf:獲取輸入接口中值的類型,如果為空返回 nil TypeOf 能傳入所有類型,是因為所有的類型都實現了空接口
package?main
import?(
?"fmt"
?"reflect"
)
func?main()??{
?var?s?interface{}?=?"abc"
?//TypeOf會返回目標的對象
?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 的相關操作
對于成員變量
先獲取 interface 的 reflect.Type,然后遍歷 NumField 再通過 reflect.Type 的 Field 獲取字段名及類型 最后通過 Field 的 interface 獲取對應的 value 對于方法
先獲取 interface 的 reflect.Type,然后遍歷 NumMethod 再通過 reflect.Type 的 t.Method 獲取真實的方法名 最后通過 Name 和 Type 獲取方法的類型和值
注意點
用于對未知類型進行遍歷探測其 Field,抽象成一個函數 go 語言里面 struct 成員變量小寫,在反射的時候直接 panic() 結構體方法名小寫是不會 panic 的,反射值也不會被查看到 指針方法是不能被反射查看到的
package?main
import?(
?"fmt"
?"reflect"
)
type?Person?struct?{
?Name?string
?Age??int
}
type?Student?struct?{
?Person?????//?匿名結構體嵌套
?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("上學了,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{})?{
?//?獲取目標對象
?t?:=?reflect.TypeOf(s)
?fmt.Printf("對象的類型名稱?%s\n",?t.Name())
?//?獲取目標對象的值類型
?v?:=?reflect.ValueOf(s)
?//?遍歷獲取成員變量
?for?i?:=?0;?i???//?Field?代表對象的字段名
??key?:=?t.Field(i)
??value?:=?v.Field(i).Interface()
??//?字段
??if?key.Anonymous?{
???fmt.Printf("匿名字段?第?%d?個字段,字段名?%s,?字段類型?%v,?字段的值?%v\n",?i+1,?key.Name,?key.Type,?value)
??}?else?{
???fmt.Printf("命名字段?第?%d?個字段,字段名?%s,?字段類型?%v,?字段的值?%v\n",?i+1,?key.Name,?key.Type,?value)
??}
?}
?//?打印方法
?for?i?:=?0;?i???m?:=?t.Method(i)
??fmt.Printf("第?%d?個方法,方法名?%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)
?/*
?對象的類型名稱?Student
?匿名字段?第?1?個字段,字段名?Person,?字段類型?main.Person,?字段的值?{geek?24}
?命名字段?第?2?個字段,字段名?StudentId,?字段類型?int,?字段的值?123
?命名字段?第?3?個字段,字段名?SchoolName,?字段類型?string,?字段的值?Beijing?University
?命名字段?第?4?個字段,字段名?Graduated,?字段類型?bool,?字段的值?true
?命名字段?第?5?個字段,字段名?Hobbies,?字段類型?[]string,?字段的值?[唱?跳?Rap]
?命名字段?第?6?個字段,字段名?Label,?字段類型?map[string]string,?字段的值?map[k1:v1?k2:v2]
?第?1?個方法,方法名?GotoSchool,?方法類型?func(main.Student)
?對象的類型名稱?Person
?命名字段?第?1?個字段,字段名?Name,?字段類型?string,?字段的值?張三
?命名字段?第?2?個字段,字段名?Age,?字段類型?int,?字段的值?100
??*/
}
3、結構體標簽和反射
json 的標簽解析出 json yaml 的標簽解析出 yaml xorm、gorm 的標簽標識數據庫 db 字段 自定義標簽 原理是 t.Field.Tag.Lookup("標簽名")
示例
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()?{
?//?對象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))
?//?從字符串解析成結構體
?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)??//?解析嚴格,考慮多余字段,忽略字段等
?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 內容
yaml_name:?李四
yaml_age:?18
yaml_city:?Shanghai
自定義標簽格式解析
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標簽,?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標簽,?key:?Name,?value:?geek,?tag:?name
?找到了aa標簽,?key:?Age,?value:?24,?tag:?age
?找到了aa標簽,?key:?City,?value:?Beijing,?tag:?city
??*/
}
4、反射調用函數
valueFunc?:=?reflect.ValueOf(Add)?//函數也是一種數據類型
typeFunc?:=?reflect.TypeOf(Add)
argNum?:=?typeFunc.NumIn()????????????//函數輸入參數的個數
args?:=?make([]reflect.Value,?argNum)?//準備函數的輸入參數
for?i?:=?0;?i??if?typeFunc.In(i).Kind()?==?reflect.Int?{
??args[i]?=?reflect.ValueOf(3)?//給每一個參數都賦3
?}
}
sumValue?:=?valueFunc.Call(args)?//返回[]reflect.Value,因為go語言的函數返回可能是一個列表
if?typeFunc.Out(0).Kind()?==?reflect.Int?{
?sum?:=?sumValue[0].Interface().(int)?//從Value轉為原始數據類型
?fmt.Printf("sum=%d\n",?sum)
}
5、反射調用方法
示例
user?:=?User{
?Id:?????7,
?Name:???"杰克遜",
?Weight:?65.5,
?Height:?1.68,
}
valueUser?:=?reflect.ValueOf(&user)??????????????//必須傳指針,因為BMI()在定義的時候它是指針的方法
bmiMethod?:=?valueUser.MethodByName("BMI")???????//MethodByName()通過Name返回類的成員變量
resultValue?:=?bmiMethod.Call([]reflect.Value{})?//無參數時傳一個空的切片
result?:=?resultValue[0].Interface().(float32)
fmt.Printf("bmi=%.2f\n",?result)
//Think()在定義的時候用的不是指針,valueUser可以用指針也可以不用指針
thinkMethod?:=?valueUser.MethodByName("Think")
thinkMethod.Call([]reflect.Value{})
valueUser2?:=?reflect.ValueOf(user)
thinkMethod?=?valueUser2.MethodByName("Think")
thinkMethod.Call([]reflect.Value{})
過程
首先通過 reflect.ValueOf(p1) 獲取得到反射類型對象 reflect.ValueOf(p1).MethodByName 需 要傳入準確的方法名稱(名稱不對會 panic: reflect: call of reflect.Value.Call on zero Value),MethodByName 代表注冊 []reflect.Value 這是最終需要調用方法的參數,無參數傳空切片 call 調用
package?main
import?(
?"fmt"
?"reflect"
)
type?Person?struct?{
?Name???string
?Age????int
?Gender?string
}
func?(p?Person)?ReflectCallFuncWithArgs(name?string,?age?int)?{
?fmt.Printf("調用的是帶參數的方法,?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("調用的是不帶參數的方法\n")
}
func?main()?{
?p1?:=?Person{
??Name:???"geek",
??Age:????24,
??Gender:?"男",
?}
?//?1.首先通過reflect.ValueOf(p1)獲取得到反射值類型
?getValue?:=?reflect.ValueOf(p1)
?//?2.帶參數的方法調用
?methodValue1?:=?getValue.MethodByName("ReflectCallFuncWithArgs")
?//?參數是reflect.Value的切片
?args1?:=?[]reflect.Value{reflect.ValueOf("張三"),?reflect.ValueOf(30)}
?methodValue1.Call(args1)
?//?3.不帶參數的方法調用
?methodValue2?:=?getValue.MethodByName("ReflectCallFuncWithNoArgs")
?//?參數是reflect.Value的切片
?args2?:=?make([]reflect.Value,?0)
?methodValue2.Call(args2)
?/*
?調用的是帶參數的方法,?args.name:?張三,?args.age:?30,?p.name:?geek,?p.age:?24
?調用的是不帶參數的方法
??*/
}
6、反射創(chuàng)建值
6.1 反射創(chuàng)建 struct
t?:=?reflect.TypeOf(User{})
value?:=?reflect.New(t)?//根據reflect.Type創(chuàng)建一個對象,得到該對象的指針,再根據指針提到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)?//把反射類型轉成go原始數據類型
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:???"李達",
?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里添加一個key-value對
mapValue.MapIndex(key).Elem().FieldByName("Name").SetString("令狐一刀")?//MapIndex?根據Key取出對應的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)??//?直接傳值會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)//會panic
valueUser.Elem().FieldByName("Weight").SetFloat(68.0)?//FieldByName()通過Name返回類的成員變量。不能在指針Value上調用FieldByName
addrValue?:=?valueUser.Elem().FieldByName("addr")
if?addrValue.CanSet()?{
?addrValue.SetString("北京")
}?else?{
?fmt.Println("addr是未導出成員,不可Set")?//以小寫字母開頭的成員相當于是私有成員
}
7.2 反射修改 slice
下面示例,間接的實現了 append 功能
users?:=?make([]*User,?1,?5)?//len=1,cap=5
sliceValue?:=?reflect.ValueOf(&users)?//準備通過Value修改users,所以傳users的地址
if?sliceValue.Elem().Len()?>?0?{??????//取得slice的長度
?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)
//調用reflect.Value的Set()函數修改其底層指向的原始數據
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)?????????????????????????????????????????????????????????//準備通過Value修改userMap,所以傳userMap的地址
mapValue.Elem().SetMapIndex(reflect.ValueOf(u2.Id),?reflect.ValueOf(u2))??????????????????????//SetMapIndex?往map里添加一個key-value對
mapValue.Elem().MapIndex(reflect.ValueOf(u1.Id)).Elem().FieldByName("Name").SetString("令狐一刀")?//MapIndex?根據Key取出對應的map
for?k,?user?:=?range?userMap?{
?fmt.Printf("key?%d?name?%s\n",?k,?user.Name)
}
See you ~
歡迎進群一起進行技術交流
加群方式:公眾號消息私信“加群”或加我好友再加群均可
評論
圖片
表情
