reflect.go包學(xué)習(xí)_之二 指針操作提高反射性能 反射應(yīng)用 反射創(chuàng)建實(shí)例
反射信息酥宴、反射調(diào)用方法、反射修改值類型,略。
Go的反射性能
為獲取同一類型不同對象狂丝,java的field是可重用的,減少了構(gòu)造和gc哗总。
//java
Field field = clazz.getField("hello");
field.get(obj1);
field.get(obj2);
相對的go:
//go
type_ := reflect.TypeOf(obj)
field, _ := type_.FieldByName("hello")
//filed對于同一類型的不同對象是相同的几颜,但無法用來獲取不同對象的此field
//每個(gè)對象的取值都要構(gòu)建reflect.Value,且不可復(fù)用讯屈,造成GC壓力
指針獲取struct字段
用指針直接操作地址蛋哭,先取field.Offset,再obj地址涮母。
獲取指針先調(diào)用unsafe.Pointer(對象地址)谆趾,再強(qiáng)轉(zhuǎn)為unitptr整數(shù)。
對象起始位置字段偏移叛本,得到字段地址沪蓬。強(qiáng)轉(zhuǎn)為字串指針(前提是已經(jīng)靜態(tài)地知道了字段的類型),再取內(nèi)容来候。
b1 := B{1,"one"}
field,_ := reflect.TypeOf(&b1).Elem().FieldByName("Name")
fieldPtr := uintptr(unsafe.Pointer(&b1))+field.Offset
*((*string)(unsafe.Pointer(fieldPtr))) = "two"
fmt.Println(*((*string)(unsafe.Pointer(fieldPtr))))
評價(jià):沒有復(fù)雜數(shù)據(jù)結(jié)構(gòu)reflect.Value的構(gòu)建跷叉。大量獲取字段,也不會(huì)觸發(fā)頻繁gc吠勘,簡單高效性芬。編程復(fù)雜,要用unsafe包操作指針剧防。
三個(gè)重要變量:起始指針植锉、字段偏移、符合字段類型的偏移后指針峭拘。
指針獲取struct字段
type interfaceHeader struct {
typ *struct{}
word unsafe.Pointer
}
structPtr := (*interfaceHeader)(unsafe.Pointer(&obj)).word
指針獲取slice字段
先構(gòu)造切片和空接口和頭struct俊庇。切片取指針狮暑,強(qiáng)轉(zhuǎn)為切片頭。如此實(shí)現(xiàn)獲取切片長度辉饱。
反射獲取元素的Type搬男。切片頭地址,加若干個(gè)元素塊大小彭沼,轉(zhuǎn)為uintptr類型(Type.Size()返回uintptr類型缔逛,所以都要轉(zhuǎn)成這個(gè)類型計(jì)算)。強(qiáng)轉(zhuǎn)為元素對應(yīng)的類型姓惑,取內(nèi)容并賦值褐奴。
指針可以強(qiáng)轉(zhuǎn),但不能計(jì)算于毙。需要計(jì)算轉(zhuǎn)為uintptr大整數(shù)敦冬。過程:轉(zhuǎn)指針,轉(zhuǎn)uintptr做計(jì)算唯沮,再轉(zhuǎn)為指針脖旱,最后強(qiáng)轉(zhuǎn)取指針指向的內(nèi)容。
slice :=[]string{"hello","world"}
header := (*SliceHeader)(unsafe.Pointer(&slice))
fmt.Println(header.Len)
//上面實(shí)現(xiàn)未用reflect.Value獲取切片長度介蛉,下面實(shí)現(xiàn)指針改值萌庆。
elementType := reflect.TypeOf(slice).Elem()//返回類型為Type
s1Ptr := uintptr(header.Data)+elementType.Size()
*((*string)(unsafe.Pointer(s1Ptr)))= "kitty"
Map暫時(shí)只用go標(biāo)準(zhǔn)庫的值反射api。
擴(kuò)展:Jsoniter甘耿,一種基于反射的 JSON 解析器踊兜。可以用 reflect.Type 得出來的信息來直接做反射佳恬,而不依賴于 reflect.ValueOf,從而提高反射效率于游。
反射創(chuàng)建slice map
功能:反射創(chuàng)建毁葱、反射添加元素、轉(zhuǎn)回Interface{}
s := make([]int,0)
sT := reflect.TypeOf(s)
sReflect := reflect.MakeSlice(sT,0,0)//反射創(chuàng)建
sReflect = reflect.Append(sReflect,reflect.ValueOf(10))//反射添加
sRtoObj := sReflect.Interface().([]int)
fmt.Println(sRtoObj)
m := make(map[int]string)
mT := reflect.TypeOf(m)
mReflect := reflect.MakeMap(mT)
mReflect.SetMapIndex(reflect.ValueOf(1),reflect.ValueOf("one"))
mRtoObj := mReflect.Interface().(map[int]string)
fmt.Println(mRtoObj)
反射創(chuàng)建實(shí)例
bT := reflect.TypeOf(b)
bIns := reflect.New(bT)
fmt.Println(bIns.Type(), bIns.Kind())
反射調(diào)用的方法含有錯(cuò)誤贰剥,可以使用斷言:
//反射調(diào)用的檢查錯(cuò)誤示例
type T struct {
}
func (this *T)HasError() (int,error) {
return 0,errors.New("hasError")
}
func main() {
t := T{}
err := reflect.ValueOf(&t).MethodByName("HasError").Call(nil)[1]
if err1 := err.Interface().(error);err1!=nil{
println(err1.Error())
}
}
用作tag作關(guān)聯(lián)倾剿。動(dòng)態(tài)調(diào)用,獲取不同類的相同字段名蚌成,并為其賦值前痘。
取Type的反射不用引用,避免調(diào)用NumField()担忧,F(xiàn)ield()都比較麻煩芹缔。Tag的值類型為字串。
Tag調(diào)用Get()直接獲取瓶盛,而調(diào)用tag.Loopup()返回tagVal,ok先作判斷最欠,防止程序panic示罗。
type A struct {
Id int `B:"Id"`
Name string `B:"Name""`
}
type B struct {
Id int
Name string
}
func main() {
a := A{1,"one"}
b := B{2,"two"}
aT := reflect.TypeOf(a)
aV := reflect.ValueOf(&a)
bV := reflect.ValueOf(&b)
for i:=0;i<aT.NumField();i++{
field := aT.Field(i)
fieldB := field.Tag.Get("B")
bV.Elem().FieldByName(fieldB).Set(aV.Elem().Field(i))
}
}
不同類型不同處理,在mvc框架中的返回值String Interface{}中有用到芝硬。
//檢測各字段類型蚜点、分別處理
for i:=0;i<aV.Elem().NumField();i++{
switch aV.Elem().Field(i).Type().Kind(){
case reflect.Int:
fmt.Println("it's Id")
case reflect.String:
fmt.Println("it's Name")
default:
}
}
IsValide檢測
//檢測是否有不存在的字段,未命中的哈希
reflect.ValueOf(s).FieldByName("").IsValid())
reflect.ValueOf(m).MapIndex(reflect.ValueOf(3)).IsValid())
其它概念
Go語言類型是靜態(tài)類型拌阴,接口也是靜態(tài)類型绍绘。
類型查詢,反射轉(zhuǎn)回Interface{}都會(huì)用到斷言迟赃。斷言和switch...case功能相同陪拘。一個(gè)簡潔,一個(gè)方便斷言失敗的default處理捺氢。
接口三定律藻丢,
反射第一定律:反射可以將“接口類型變量”轉(zhuǎn)換為“反射類型對象”。
反射第二定律:反射可以將“反射類型對象”轉(zhuǎn)換為“接口類型變量”摄乒。
反射第三定律:如果要修改“反射類型對象”悠反,其值必須是“可寫的”(settable)。用傳址+Elem()的方法馍佑。
最后的整合全部實(shí)例
package main
import (
"errors"
"fmt"
"reflect"
"unsafe"
)
type T struct {
}
func (this *T) HasError() (int, error) {
return 0, errors.New("hasError")
}
type A struct {
Id int `B:"Id"`
Name string `B:"Name""`
}
type B struct {
Id int
Name string
}
func main() {
t := T{}
err := reflect.ValueOf(&t).MethodByName("HasError").Call(nil)[1]
if err1 := err.Interface().(error); err1 != nil {
println(err1.Error())
}
a := A{1, "one"}
b := B{2, "two"}
aT := reflect.TypeOf(a)
aV := reflect.ValueOf(&a)
bV := reflect.ValueOf(&b)
for i := 0; i < aT.NumField(); i++ {
field := aT.Field(i)
fieldB := field.Tag.Get("B")
bV.Elem().FieldByName(fieldB).Set(aV.Elem().Field(i))
}
for i := 0; i < aV.Elem().NumField(); i++ {
switch aV.Elem().Field(i).Type().Kind() {
case reflect.Int:
fmt.Println("it's Id")
case reflect.String:
fmt.Println("it's Name")
default:
}
}
s := make([]int, 0)
sT := reflect.TypeOf(s)
sReflect := reflect.MakeSlice(sT, 0, 0) //反射創(chuàng)建
sReflect = reflect.Append(sReflect, reflect.ValueOf(10)) //反射添加
sRtoObj := sReflect.Interface().([]int)
fmt.Println(sRtoObj)
m := make(map[int]string)
mT := reflect.TypeOf(m)
mReflect := reflect.MakeMap(mT)
mReflect.SetMapIndex(reflect.ValueOf(1), reflect.ValueOf("one"))
mRtoObj := mReflect.Interface().(map[int]string)
fmt.Println(mRtoObj)
b1 := B{1, "one"}
field, _ := reflect.TypeOf(&b1).Elem().FieldByName("Name")
fieldPtr := uintptr(unsafe.Pointer(&b1)) + field.Offset
*((*string)(unsafe.Pointer(fieldPtr))) = "two"
fmt.Println(*((*string)(unsafe.Pointer(fieldPtr))))
slice := []string{"hello", "world"}
header := (*SliceHeader)(unsafe.Pointer(&slice))
fmt.Println(header.Len)
//上面實(shí)現(xiàn)未用reflect.Value獲取切片長度斋否,下面實(shí)現(xiàn)指針改值。
elementType := reflect.TypeOf(slice).Elem() //返回類型為Type
s1Ptr := uintptr(header.Data) + elementType.Size()
*((*string)(unsafe.Pointer(s1Ptr))) = "kitty"
bT := reflect.TypeOf(b)
bIns := reflect.New(bT)
fmt.Println(bIns.Type(), bIns.Kind())
}
type SliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
type interfaceHeader struct {
typ *struct{}
word unsafe.Pointer
}
https://studygolang.com/articles/14577?fr=sidebar
https://studygolang.com/articles/12348?fr=sidebar
https://studygolang.com/articles/12349
https://www.jb51.net/article/90021.htm