<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反射-下篇

          共 2263字,需瀏覽 5分鐘

           ·

          2021-11-30 04:30

          目錄

          • 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 ~

          歡迎進群一起進行技術交流

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

          瀏覽 110
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  亚洲免费免费在线观看 | 天天干天天摸天天操 | 天堂视频在线 | 天天拍拍日日拍拍 | 欧美另类在线观看 |