reflect.go包學(xué)習(xí)_之二 指針操作提高反射性能 反射應(yīng)用 反射創(chuàng)建實(shí)例

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

https://blog.csdn.net/xhd731568849/article/details/79198048

https://www.cnblogs.com/wdliu/p/9222283.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拭荤,一起剝皮案震驚了整個(gè)濱河市茵臭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舅世,老刑警劉巖旦委,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雏亚,居然都是意外死亡缨硝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進(jìn)店門罢低,熙熙樓的掌柜王于貴愁眉苦臉地迎上來查辩,“玉大人,你說我怎么就攤上這事网持∫说海” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵功舀,是天一觀的道長萍倡。 經(jīng)常有香客問我,道長日杈,這世上最難降的妖魔是什么遣铝? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任佑刷,我火速辦了婚禮,結(jié)果婚禮上酿炸,老公的妹妹穿的比我還像新娘瘫絮。我一直安慰自己,他們只是感情好填硕,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布麦萤。 她就那樣靜靜地躺著,像睡著了一般扁眯。 火紅的嫁衣襯著肌膚如雪壮莹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天姻檀,我揣著相機(jī)與錄音命满,去河邊找鬼。 笑死绣版,一個(gè)胖子當(dāng)著我的面吹牛胶台,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杂抽,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼诈唬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缩麸?” 一聲冷哼從身側(cè)響起铸磅,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杭朱,沒想到半個(gè)月后阅仔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弧械,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年霎槐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梦谜。...
    茶點(diǎn)故事閱讀 40,438評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖袭景,靈堂內(nèi)的尸體忽然破棺而出唁桩,到底是詐尸還是另有隱情,我是刑警寧澤耸棒,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布荒澡,位于F島的核電站,受9級特大地震影響与殃,放射性物質(zhì)發(fā)生泄漏单山。R本人自食惡果不足惜碍现,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望米奸。 院中可真熱鬧昼接,春花似錦、人聲如沸悴晰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铡溪。三九已至漂辐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棕硫,已是汗流浹背髓涯。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哈扮,地道東北人纬纪。 一個(gè)月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像灶泵,于是被迫代替她去往敵國和親育八。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1.安裝 https://studygolang.com/dl 2.使用vscode編輯器安裝go插件 3.go語...
    go含羞草閱讀 1,556評論 0 6
  • 首先巴拉巴拉一下golang反射機(jī)制的三個(gè)定律 1.反射可以從接口類型到反射類型對象 2.反射可以從反射類型對象到...
    吃貓的魚0閱讀 2,919評論 0 1
  • 轉(zhuǎn)載自:https://halfrost.com/go_map_chapter_one/ https://half...
    HuJay閱讀 6,157評論 1 5
  • 紅樓別夜堪丑悵赦邻,頓覺游絲量髓棋。行船行至客江中,江闊云低惶洲,何夜照悲風(fēng)按声。悲風(fēng)已過長洲宛,長洲宛里見姮娥恬吕。姮娥體態(tài)真善美签则,...
    王玉笙閱讀 548評論 0 0
  • 我知道我做錯(cuò)了,從哪里開始呢铐料,我又到底做錯(cuò)了什么呢渐裂? 2017年初,因?yàn)樽錾馔顿Y失敗而破產(chǎn)的老爸帶我去幫他做點(diǎn)貸...
    GENGELAINE閱讀 215評論 0 0