Go reflect包用法和理解

reflect包中的具体方法

 

reflect.TypeOfreflect.ValueOf可以将一个普通的变量转换成『反射』包中提供的TypeValue

type Person struct {
    Name     string
    Sex      string
}
func (p *Person) Add(a, b string) {
    fmt.Printf("me %+v\n", a+b)
}
func (p *Person) AddRes(a, b string) (string, error){
    if b == "boy" {
        return a + b, errors.New("its a boy")
    }
    return a+b, nil
}
func main() {
    funcName := "Add"
    typeT := &Person{}
    a := reflect.ValueOf("li")
    b := reflect.ValueOf("femal")
    in := []reflect.Value{a, b}
    reflect.ValueOf(typeT).MethodByName(funcName).Call(in)//如果是没有参数的函数就需要传nil
    //me lifemal

    c := reflect.ValueOf("J")
    d := reflect.ValueOf("boy")
    inr := []reflect.Value{c, d}
    funcName = "AddRes"
    ret := reflect.ValueOf(typeT).MethodByName("AddRes").Call(inr)
    fmt.Printf("ret is %+v\n", ret)
    //ret is [Jboy <error Value>]
    for i := 0; i < len(ret); i++ {
        fmt.Printf("ret index:%+v, type:%+v, value:%+v\n", i, ret[i].Kind(), ret[i].Interface())
    }
    //ret index:0, type:string, value:Jboy
    //ret index:1, type:interface, value:its a boy
}

通过reflect修改值,主要包括(指针指向的具体元素,slice的元素,结构体指针的字段,数组指针的元素)

  • 取地址:v := reflect.ValueOf(&x)

  • CanSet()来判断是否可以设置值

  • 判断v.Elem()是否可以设值

  • v.Elem()设置具体值

ts := []int{1, 2, 3}
tsV := reflect.ValueOf(ts)
if tsV.Index(0).CanSet() {
     tsV.Index(0).Set(reflect.ValueOf(10))
}
fmt.Printf("ts is %+v\n", ts)
//10,2,3
tsA := [3]int{1, 2, 3}
tsAv := reflect.ValueOf(&tsA)
if tsAv.Elem().Index(0).CanSet() {
     tsAv.Elem().Index(0).Set(reflect.ValueOf(10))
}
fmt.Printf("tsA is %+v\n", tsA)

//小tips,切片返回的就是地址
func main() {
    ary := [3]int{1, 2, 3}//数组
    sli := []int{1, 2, 3}//切片
    a := ary
    b := sli
    fmt.Printf("ary is %p\nsli is %p\na is %p\nb is %p\n", &ary,sli,&a,b)
}
// ary is 0xc0000b6020
// sli is 0xc0000b6040
// a is 0xc0000b6060
// b is 0xc0000b6040

通过reflect判断某个实例是否实现了接口

typeT := &Struct{}
IF := reflect.TypeOf((*Interface)(nil)).Elem()
tv := reflect.TypeOf(typeT)
tv.Implements(IF)//true

reflect对struct中的tag解析

获取tag中的值

type TagTest struct {
     Name string `json:"name_json"`
     Age  int    `json:"age_json"`
}
t := TagTest{Name: "tom", Age: 10}
rtt := reflect.TypeOf(t)
for i := 0; i < rtt.NumField(); i++ {
     field := rtt.Field(i)
     if json, ok := field.Tag.Lookup("json"); ok {
     fmt.Printf("tag is %+v, value is %+v\n", json,field.Tag.Get("json"))
     }
}

field.Tag.Lookup()和field.Tag.Get()方法都是取tag的值,只不过Lookup会用第二个返回值返回是否存在这个tag,而Get方法若不存在这个tag会返回一个空字符串

Name string json:"name,omitempty" omitempty表示如果字段为空,就忽略这个字段

实例解析json数据获取tag

package main

import (
    "fmt"
    "reflect"
    "strings"
)

type Person struct {
    Name        string `label:"Person Name: " uppercase:"true"`
    Age         int    `label:"Age is: "`
    Sex         string `label:"Sex is: "`
    Description string
}

// 按照tag打印结构体
func PrintUseTag(ptr interface{}) error {

    // 获取入参的类型
    t := reflect.TypeOf(ptr)
    // 入参类型校验
    if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
        return fmt.Errorf("参数应该为结构体指针")
    }

    // 取指针指向的结构体变量
    v := reflect.ValueOf(ptr).Elem()
    // 解析字段
    for i := 0; i < v.NumField(); i++ {
        // 取tag
        fieldInfo := v.Type().Field(i)
        tag := fieldInfo.Tag

        // 解析label tag
        label := tag.Get("label")
        if label == "" {
            label = fieldInfo.Name + ": "
        }

        // 解析uppercase tag
        value := fmt.Sprintf("%v", v.Field(i))
        if fieldInfo.Type.Kind() == reflect.String {
            uppercase := tag.Get("uppercase")
            if uppercase == "true" {
                value = strings.ToUpper(value)
            } else {
                value = strings.ToLower(value)
            }
        }

        fmt.Println(label + value)
    }
    return nil
}
func main() {
    person := Person{
        Name:        "Tom",
        Age:         29,
        Sex:         "Male",
        Description: "Cool",
    }
    PrintUseTag(&person)
}

 

posted @ 2020-07-30 20:21  LeeJuly  阅读(654)  评论(0编辑  收藏  举报