<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          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 是否實(shí)現(xiàn)接口

            • 6.7 value 和其他類型的互換

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


          1、反射的定義

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

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

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

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

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

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

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

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

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


          3、Type

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

          源碼go/src/reflect/type.go

          type?Type?interface?{
          ?Align()?int
          ?FieldAlign()?int
          ?Method(int)?Method??//?第?i?個(gè)方法
          ?MethodByName(string)?(Method,?bool)??//?根據(jù)名稱獲取方法
          ?NumMethod()?int??//?方法的個(gè)數(shù)
          ?Name()?string??//?獲取結(jié)構(gòu)體名稱
          ?PkgPath()?string??//?包路徑
          ?Size()?uintptr??//?占用內(nèi)存的大小
          ?String()?string??//?獲取字符串表述
          ?Kind()?Kind?//?數(shù)據(jù)類型
          ?Implements(u?Type)?bool??//?判斷是否實(shí)現(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個(gè)成員
          ?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ù)的個(gè)數(shù)
          ?NumOut()?int??//?返回參數(shù)的個(gè)數(shù)
          ?Out(i?int)?Type

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

          4、Value

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

          源碼go/src/reflect/value.go

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

          5、反射三大定律

          interface類型有個(gè)valuetype對(duì),而反射就是檢查interface的這個(gè)value, type對(duì)的 具體一點(diǎn)說就是Go提供一組方法提取interfacevalue,提供另一組方法提取interfacetype

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

          5.1 反射第一定律

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

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

          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

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

          5.2 反射第二定律

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

          之所以叫’反射’,反射對(duì)象與interface對(duì)象是可以互相轉(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)
          }

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

          5.3 反射第三定律

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

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

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

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

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

          錯(cuò)誤原因即是v是不可修改的。

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

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

          想到此處,即可明白,如果構(gòu)建v時(shí)使用x的地址就可實(shí)現(xiàn)修改了,但此時(shí)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()???????????//成員變量的個(gè)數(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,??????????//相對(duì)于結(jié)構(gòu)體首地址的內(nèi)存偏移量,string類型會(huì)占據(jù)16個(gè)字節(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ù)是個(gè)slice,因?yàn)橛衧truct嵌套的情況
          fmt.Printf("third?field?name?%s\n",?thirdField.Name)
          }

          6.3 獲取 method 信息

          typeUser?:=?reflect.TypeOf(common.User{})
          methodNum?:=?typeUser.NumMethod()?//成員方法的個(gè)數(shù)。接收者為指針的方法【不】包含在內(nèi)
          for?i?:=?0;?i??method?:=?typeUser.Method(i)
          ?fmt.Println(method.Type)??//?會(huì)輸出函數(shù)完整的簽名,其中輸入?yún)?shù)會(huì)將結(jié)構(gòu)體本身也作為輸入?yún)?shù),因此參數(shù)個(gè)數(shù)多一個(gè)
          ?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()?//成員方法的個(gè)數(shù)。接收者為指針或值的方法都包含在內(nèi),也就是說值實(shí)現(xiàn)的方法指針也實(shí)現(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ù)的個(gè)數(shù)
          argOutNum?:=?typeFunc.NumOut()?//輸出參數(shù)的個(gè)數(shù)
          for?i?:=?0;?i??argTyp?:=?typeFunc.In(i)
          ?fmt.Printf("第%d個(gè)輸入?yún)?shù)的類型%s\n",?i,?argTyp)
          }
          for?i?:=?0;?i??argTyp?:=?typeFunc.Out(i)
          ?fmt.Printf("第%d個(gè)輸出參數(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 是否實(shí)現(xiàn)接口

          //通過reflect.TypeOf((*)(nil)).Elem()獲得接口類型。因?yàn)镻eople是個(gè)接口不能創(chuàng)建實(shí)例,所以把nil強(qiáng)制轉(zhuǎn)為*common.People類型
          typeOfPeople?:=?reflect.TypeOf((*common.People)(nil)).Elem()??//?可以將nil理解成People指針的一個(gè)實(shí)例
          fmt.Printf("typeOfPeople?kind?is?interface?%t\n",?typeOfPeople.Kind()?==?reflect.Interface)
          t1?:=?reflect.TypeOf(common.User{})
          t2?:=?reflect.TypeOf(&common.User{})
          //User的值類型實(shí)現(xiàn)了接口,則指針類型也實(shí)現(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 判斷空值的三種情況

          pointerchannelfuncinterfacemapslice的預(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指向一個(gè)nil
          if?v.IsValid()?{
          ?fmt.Printf("v持有的值是nil?%t\n",?v.IsNil())?//調(diào)用IsNil()前先確保IsValid(),否則會(huì)panic??//?true
          }

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

          See you ~

          參考資料

          [1]

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

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

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

          瀏覽 84
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天干天天射天天舔 | 国产中文自拍 | 豆花官网进入免费操逼 | 中文字幕无码毛片免费看 | 久久娱乐精品 |