1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Golang反射-上篇

        共 4239字,需瀏覽 9分鐘

         ·

        2021-11-14 10:39

        目錄

        • 1、反射的定義

        • 2、反射的基礎(chǔ)數(shù)據(jù)類型

        • 3、Type

        • 4、Value

        • 5、反射三大定律

          • 5.1 反射第一定律

          • 5.2 反射第二定律

          • 5.3 反射第三定律

        • 6、反射常用的 API

          • 6.1 獲取 type 類型

          • 6.2 獲取 Field 信息

          • 6.3 獲取 method 信息

          • 6.4 獲取函數(shù)信息

          • 6.5 賦值和轉(zhuǎn)換關(guān)系

          • 6.6 是否實現(xiàn)接口

          • 6.7 value 和其他類型的互換

          • 6.8 value 判斷空值的三種情況


        1、反射的定義

        It’s a great source of confusion ~ (引用自官方博客)

        反射是指在運行時動態(tài)的訪問和修改任意類型對象的結(jié)構(gòu)和成員,在go語言中提供reflect包提供反射的功能,每一個變量都有兩個屬性:類型Type和值Value

        反射能夠自描述自控制 例如python的反射:根據(jù)字符串執(zhí)行函數(shù),根據(jù)字符串導(dǎo)入包

        go是靜態(tài)語言,反射就是go提供的一種機(jī)制,在編譯時不知道類型的情況下可以做如下的事情

        • 更新變量
        • 運行時查看值
        • 調(diào)用方法
        • 對他們的布局進(jìn)行操作

        使用反射的兩個經(jīng)典場景

        • 你編寫的這個函數(shù),還不知道傳給你的類型具體是什么,可能是還沒約定好,也可能是傳入的類型很多

        • 希望通過用戶的輸入來決定調(diào)用哪個函數(shù)(根據(jù)字符串調(diào)用方法),動態(tài)執(zhí)行函數(shù)

        2、反射的基礎(chǔ)數(shù)據(jù)類型


        3、Type

        reflect.Type是一個接口類型,用于獲取變量類型相關(guān)的信息,可通過reflect.TypeOf函數(shù)獲取某個變量的類型信息

        源碼go/src/reflect/type.go

        type?Type?interface?{
        ?Align()?int
        ?FieldAlign()?int
        ?Method(int)?Method??//?第?i?個方法
        ?MethodByName(string)?(Method,?bool)??//?根據(jù)名稱獲取方法
        ?NumMethod()?int??//?方法的個數(shù)
        ?Name()?string??//?獲取結(jié)構(gòu)體名稱
        ?PkgPath()?string??//?包路徑
        ?Size()?uintptr??//?占用內(nèi)存的大小
        ?String()?string??//?獲取字符串表述
        ?Kind()?Kind?//?數(shù)據(jù)類型
        ?Implements(u?Type)?bool??//?判斷是否實現(xiàn)了某接口
        ?AssignableTo(u?Type)?bool??//?能否賦給另外一種類型
        ?ConvertibleTo(u?Type)?bool??//?能否轉(zhuǎn)換為另外一種類型
        ?Comparable()?bool
        ?Bits()?int
        ?ChanDir()?ChanDir
        ?IsVariadic()?bool
        ?Elem()?Type??//?解析指針(指針類型轉(zhuǎn)為普通類型)
        ?Field(i?int)?StructField??//?第i個成員
        ?FieldByIndex(index?[]int)?StructField??//?根據(jù)index路徑獲取嵌套成員
        ?FieldByName(name?string)?(StructField,?bool)??//?根據(jù)名稱獲取成員
        ?FieldByNameFunc(match?func(string)?bool)?(StructField,?bool)
        ?In(i?int)?Type
        ?Key()?Type
        ?Len()?int??//?容器的長度
        ?NumField()?int
        ?NumIn()?int??//?輸出參數(shù)的個數(shù)
        ?NumOut()?int??//?返回參數(shù)的個數(shù)
        ?Out(i?int)?Type

        ?common()?*rtype
        ?uncommon()?*uncommonType
        }

        4、Value

        reflect.Value是一個結(jié)構(gòu)體類型,用于獲取變量值的信息,可通過reflect.ValueOf函數(shù)獲取修改原始數(shù)據(jù)類型(某個變量)的值信息

        源碼go/src/reflect/value.go

        type?Value?struct?{
        ???//?代表的數(shù)據(jù)類型
        ?typ?*rtype
        ?//?指向原始數(shù)據(jù)的指針
        ?ptr?unsafe.Pointer
        }

        5、反射三大定律

        interface類型有個value,type對,而反射就是檢查interface的這個value, type對的 具體一點說就是Go提供一組方法提取interfacevalue,提供另一組方法提取interfacetype

        • reflect.Type提供一組接口處理interface的類型,即value, type中的type
        • reflect.Value提供一組接口處理interface的值,即value, type中的value

        5.1 反射第一定律

        反射第一定律:反射可以將interface類型變量轉(zhuǎn)換成反射對象

        如何通過反射獲取一個變量的值和類型

        package?main
        import?(
        ????"fmt"
        ????"reflect"
        )
        func?main()?{
        ????var?x?float64?=?3.4
        ????t?:=?reflect.TypeOf(x)??//t?is?reflext.Type
        ????fmt.Println("type:",?t)
        ????fmt.Println("kind?is?float64:",?v.Kind()?==?reflect.Float64)
        ????v?:=?reflect.ValueOf(x)?//v?is?reflext.Value
        ????fmt.Println("value:",?v)
        }

        程序輸出

        type:?float64
        kind?is?float64:?true
        value:?3.4

        反射是針對interface類型變量的,其中TypeOf()ValueOf()接受的參數(shù)都是interface{}類型的,也即x值是被轉(zhuǎn)成了interface傳入的

        5.2 反射第二定律

        反射第二定律:反射可以將反射對象還原成interface對象

        之所以叫’反射’,反射對象與interface對象是可以互相轉(zhuǎn)化的

        package?main
        import?(
        ????"fmt"
        ????"reflect"
        )
        func?main()?{
        ????var?x?float64?=?3.4
        ????v?:=?reflect.ValueOf(x)?//v?is?reflext.Value
        ????var?y?float64?=?v.Interface().(float64)
        ????fmt.Println("value:",?y)
        }

        對象x轉(zhuǎn)換成反射對象vv又通過Interface()接口轉(zhuǎn)換成interface對象,interface對象通過.(float64)類型斷言獲取float64類型的值

        5.3 反射第三定律

        反射第三定律:反射對象可修改,value值必須是可設(shè)置的

        通過反射可以將interface類型變量轉(zhuǎn)換成反射對象,可以使用該反射對象設(shè)置其持有的值

        package?main
        import?(
        ????"reflect"
        )
        func?main()?{
        ????var?x?float64?=?3.4
        ????v?:=?reflect.ValueOf(x)
        ????v.SetFloat(7.1)?//?Error:?will?panic.
        }

        通過反射對象 v 設(shè)置新值,會出現(xiàn)panic

        panic:?reflect:?reflect.Value.SetFloat?using?unaddressable?value

        錯誤原因即是v是不可修改的。

        反射對象是否可修改取決于其所存儲的值,回想一下函數(shù)傳參時是傳值還是傳址就不難理解上例中為何失敗了。

        上面例子傳入reflect.ValueOf()函數(shù)的其實是x的值,而非x本身。即通過v修改其值是無法影響x的,也即是無效的修改,所以golang會報錯

        想到此處,即可明白,如果構(gòu)建v時使用x的地址就可實現(xiàn)修改了,但此時v代表的是指針地址,我們要設(shè)置的是指針?biāo)赶虻膬?nèi)容,也即我們想要修改的是*v。通過v修改x的值?

        reflect.Value提供了Elem()方法,可以獲得指針向指向的value

        package?main
        import?(
        "reflect"
        ????"fmt"
        )
        func?main()?{
        ????var?x?float64?=?3.4
        ????v?:=?reflect.ValueOf(&x)
        ????v.Elem().SetFloat(7.1)
        ????fmt.Println("x?:",?v.Elem().Interface())
        }

        輸出為:

        x?:?7.1

        6、反射常用的 API

        6.1 獲取 type 類型

        typeUser?:=?reflect.TypeOf(&User{})?//通過TypeOf()得到Type類型
        fmt.Println(typeUser)??????????????????????//*User
        fmt.Println(typeUser.Elem())???????????????//User
        fmt.Println(typeUser.Name())???????????????//空字符串
        fmt.Println(typeUser.Elem().Name())????????//User,不帶包名的類名稱
        fmt.Println(typeUser.Kind())???????????????//ptr
        fmt.Println(typeUser.Elem().Kind())????????//struct
        fmt.Println(typeUser.Kind()?==?reflect.Ptr)
        fmt.Println(typeUser.Elem().Kind()?==?reflect.Struct)

        6.2 獲取 Field 信息

        typeUser?:=?reflect.TypeOf(User{})?//需要用struct的Type,不能用指針的Type
        fieldNum?:=?typeUser.NumField()???????????//成員變量的個數(shù)
        for?i?:=?0;?i??field?:=?typeUser.Field(i)
        ?fmt.Printf("%d?%s?offset?%d?anonymous?%t?type?%s?exported?%t?json?tag?%s\n",?i,
        ??field.Name,????????????//變量名稱
        ??field.Offset,??????????//相對于結(jié)構(gòu)體首地址的內(nèi)存偏移量,string類型會占據(jù)16個字節(jié)
        ??field.Anonymous,???????//是否為匿名成員
        ??field.Type,????????????//數(shù)據(jù)類型,reflect.Type類型
        ??field.IsExported(),????//包外是否可見(即是否以大寫字母開頭)
        ??field.Tag.Get("json"))?//獲取成員變量后面``里面定義的tag
        //可以通過FieldByName獲取Field
        if?nameField,?ok?:=?typeUser.FieldByName("Name");?ok?{
        ?fmt.Printf("Name?is?exported?%t\n",?nameField.IsExported())
        }
        //也可以根據(jù)FieldByIndex獲取Field
        thirdField?:=?typeUser.FieldByIndex([]int{2})?//參數(shù)是個slice,因為有struct嵌套的情況
        fmt.Printf("third?field?name?%s\n",?thirdField.Name)
        }

        6.3 獲取 method 信息

        typeUser?:=?reflect.TypeOf(common.User{})
        methodNum?:=?typeUser.NumMethod()?//成員方法的個數(shù)。接收者為指針的方法【不】包含在內(nèi)
        for?i?:=?0;?i??method?:=?typeUser.Method(i)
        ?fmt.Println(method.Type)??//?會輸出函數(shù)完整的簽名,其中輸入?yún)?shù)會將結(jié)構(gòu)體本身也作為輸入?yún)?shù),因此參數(shù)個數(shù)多一個
        ?fmt.Printf("method?name:%s?,type:%s,?exported:%t\n",?method.Name,?method.Type,?method.IsExported())
        }
        fmt.Println()
        if?method,?ok?:=?typeUser.MethodByName("Examine2");?ok?{??//?根據(jù)方法名獲取
        ?fmt.Printf("method?name:%s?,type:%s,?exported:%t\n",?method.Name,?method.Type,?method.IsExported())
        }
        typeUser2?:=?reflect.TypeOf(&common.User{})
        methodNum?=?typeUser2.NumMethod()?//成員方法的個數(shù)。接收者為指針或值的方法都包含在內(nèi),也就是說值實現(xiàn)的方法指針也實現(xiàn)了(反之不成立)
        for?i?:=?0;?i??method?:=?typeUser2.Method(i)
        ?fmt.Printf("method?name:%s?,type:%s,?exported:%t\n",?method.Name,?method.Type,?method.IsExported())
        }

        6.4 獲取函數(shù)信息

        typeFunc?:=?reflect.TypeOf(Add)?//獲取函數(shù)類型
        fmt.Printf("is?function?type?%t\n",?typeFunc.Kind()?==?reflect.Func)
        argInNum?:=?typeFunc.NumIn()???//輸入?yún)?shù)的個數(shù)
        argOutNum?:=?typeFunc.NumOut()?//輸出參數(shù)的個數(shù)
        for?i?:=?0;?i??argTyp?:=?typeFunc.In(i)
        ?fmt.Printf("第%d個輸入?yún)?shù)的類型%s\n",?i,?argTyp)
        }
        for?i?:=?0;?i??argTyp?:=?typeFunc.Out(i)
        ?fmt.Printf("第%d個輸出參數(shù)的類型%s\n",?i,?argTyp)
        }

        6.5 賦值和轉(zhuǎn)換關(guān)系

        • type1.AssignableTo(type2) // type1 代表的類型是否可以賦值給 type2 代表的類型
        • type1.ConvertibleTo(type2)) // type1 代表的類型是否可以轉(zhuǎn)換成 type2 代表的類型
        • java 的反射可以獲取繼承關(guān)系,而 go 語言不支持繼承,所以必須是相同的類型才能 AssignableTo 和 ConvertibleTo

        示例

        u?:=?reflect.TypeOf(User{})
        t?:=?reflect.TypeOf(Student{})?//Student內(nèi)部嵌套了User
        u2?:=?reflect.TypeOf(User{})

        //false?false
        fmt.Println(t.AssignableTo(u))??//t代表的類型是否可以賦值給u代表的類型
        fmt.Println(t.ConvertibleTo(u))?//t代表的類型是否可以轉(zhuǎn)換成u代表的類型

        //false?false
        fmt.Println(u.AssignableTo(t))
        fmt.Println(u.ConvertibleTo(t))

        //true?true
        fmt.Println(u.AssignableTo(u2))
        fmt.Println(u.ConvertibleTo(u2))

        6.6 是否實現(xiàn)接口

        //通過reflect.TypeOf((*)(nil)).Elem()獲得接口類型。因為People是個接口不能創(chuàng)建實例,所以把nil強(qiáng)制轉(zhuǎn)為*common.People類型
        typeOfPeople?:=?reflect.TypeOf((*common.People)(nil)).Elem()??//?可以將nil理解成People指針的一個實例
        fmt.Printf("typeOfPeople?kind?is?interface?%t\n",?typeOfPeople.Kind()?==?reflect.Interface)
        t1?:=?reflect.TypeOf(common.User{})
        t2?:=?reflect.TypeOf(&common.User{})
        //User的值類型實現(xiàn)了接口,則指針類型也實現(xiàn)了接口;但反過來不行(把Think的接收者改為*User試試)
        fmt.Printf("t1?implements?People?interface?%t\n",?t1.Implements(typeOfPeople))??//?false
        fmt.Printf("t2?implements?People?interface?%t\n",?t2.Implements(typeOfPeople))??//?true

        6.7 value 和其他類型的互換

        //原始類型轉(zhuǎn)為Value
        iValue?:=?reflect.ValueOf(1)
        sValue?:=?reflect.ValueOf("hello")
        userPtrValue?:=?reflect.ValueOf(&common.User{
        ?Id:?????7,
        ?Name:???"杰克遜",
        ?Weight:?65,
        ?Height:?1.68,
        })
        fmt.Println(iValue)???????//1
        fmt.Println(sValue)???????//hello
        fmt.Println(userPtrValue)?//&{7?杰克遜??65?1.68}
        //Value轉(zhuǎn)為Type
        iType?:=?iValue.Type()
        sType?:=?sValue.Type()
        userType?:=?userPtrValue.Type()
        //在Type和相應(yīng)Value上調(diào)用Kind()結(jié)果一樣的
        fmt.Println(iType.Kind()?==?reflect.Int,?iValue.Kind()?==?reflect.Int,?iType.Kind()?==?iValue.Kind())???????????????????//true?true
        fmt.Println(sType.Kind()?==?reflect.String,?sValue.Kind()?==?reflect.String,?sType.Kind()?==?sValue.Kind())?????????????//true?true
        fmt.Println(userType.Kind()?==?reflect.Ptr,?userPtrValue.Kind()?==?reflect.Ptr,?userType.Kind()?==?userPtrValue.Kind())?//true?true?true

        //指針Value和非指針Value互相轉(zhuǎn)換
        userValue?:=?userPtrValue.Elem()????????????????????//Elem()?指針Value轉(zhuǎn)為非指針Value
        fmt.Println(userValue.Kind(),?userPtrValue.Kind())??//struct?ptr
        userPtrValue3?:=?userValue.Addr()???????????????????//Addr()?非指針Value轉(zhuǎn)為指針Value
        fmt.Println(userValue.Kind(),?userPtrValue3.Kind())?//struct?ptr

        //轉(zhuǎn)為原始類型
        //通過Interface()函數(shù)把Value轉(zhuǎn)為interface{},再從interface{}強(qiáng)制類型轉(zhuǎn)換,轉(zhuǎn)為原始數(shù)據(jù)類型
        //或者在Value上直接調(diào)用Int()、String()等一步到位
        fmt.Printf("origin?value?iValue?is?%d?%d\n",?iValue.Interface().(int),?iValue.Int())
        fmt.Printf("origin?value?sValue?is?%s?%s\n",?sValue.Interface().(string),?sValue.String())
        user?:=?userValue.Interface().(common.User)
        fmt.Printf("id=%d?name=%s?weight=%.2f?height=%.2f\n",?user.Id,?user.Name,?user.Weight,?user.Height)
        user2?:=?userPtrValue.Interface().(*common.User)
        fmt.Printf("id=%d?name=%s?weight=%.2f?height=%.2f\n",?user2.Id,?user2.Name,?user2.Weight,?user2.Height)

        6.8 value 判斷空值的三種情況

        pointer、channel、funcinterface、map、slice的預(yù)先聲明都是nil

        var?i?interface{}?//接口沒有指向具體的值
        v?:=?reflect.ValueOf(i)
        fmt.Printf("v持有值?%t,?type?of?v?is?Invalid?%t\n",?v.IsValid(),?v.Kind()?==?reflect.Invalid)??//?false

        var?user?*common.User?=?nil
        v?=?reflect.ValueOf(user)?//Value指向一個nil
        if?v.IsValid()?{
        ?fmt.Printf("v持有的值是nil?%t\n",?v.IsNil())?//調(diào)用IsNil()前先確保IsValid(),否則會panic??//?true
        }

        var?u?common.User?//只聲明,里面的值都是0值
        v?=?reflect.ValueOf(u)
        if?v.IsValid()?{
        ?fmt.Printf("v持有的值是對應(yīng)類型的0值?%t\n",?v.IsZero())?//調(diào)用IsZero()前先確保IsValid(),否則會panic??//?true
        }

        See you ~

        參考資料

        [1]

        https://go.dev/blog/laws-of-reflection

        歡迎進(jìn)群一起進(jìn)行技術(shù)交流

        加群方式:公眾號消息私信“加群或加我好友再加群均可

        瀏覽 84
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            免费一级婬A片AAA毛片古女 | 嫩逼成人网 | 精品人妻无码一区二区三区不卡 | 久久大香 | 男人操女人网站 | 女人扒开腿秘 免费网站视频 | 双儿在军营轮流排火故事背景 | 午夜男女免费看大片 | 国产又大又猛 | 日韩一级黄色毛片 |