美文网首页
go 的反射 reflect

go 的反射 reflect

作者: OOM_Killer | 来源:发表于2019-08-06 23:57 被阅读0次

    Golang语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。
    grpc 的 golang 版本也是靠反射实现的。

    reflect 的基本功能

    • reflect.TypeOf 返回类型 reflect.Type
    • reflect.ValueOf 返回值 reflect.Value
    • 可以从reflect.Value 中获取类型
    • 通过Kind 来判断类型

    先看第一个例子

    一 、 反射的基本用法

    ValueOf用来获取输入参数接口中的数据的值,如果接口为空则返回0
    TypeOf用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil

    // 反射的基础用法
    func base00() {
        var num = 1.2345
        fmt.Println("type: ",reflect.TypeOf(num))       
        fmt.Println("value: ",reflect.ValueOf(num))     
    }
    
    运行结果:
    type : float64
    value : 1.234
    
    二 、 反射来判断参数类型

    以下两种方法可以判断 传入参数的类型,动态进行判断。

    func base01(v interface{}) {
        t := reflect.TypeOf(v)
        switch t.Kind() {
        case reflect.Float32,reflect.Float64:
            fmt.Println("float")
        case reflect.Int16,reflect.Int32,reflect.Int64:
            fmt.Println("int")
        case reflect.String:
            fmt.Println("string")
        default:
            fmt.Println("none")
        }
    
        switch v.(type) {   // 方法二
        case float64,float32:
            fmt.Println("float")
        case int32,int64:
            fmt.Println("int")
        case string:
            fmt.Println("none")
        default:
            fmt.Println("none")
        }
    }
    
    三 、 通过interface 获取到真实的值

    当执行reflect.ValueOf(interface)之后,就得到了一个类型为”relfect.Value”变量,可以通过它本身的Interface()方法获得接口变量的真实内容,然后可以通过类型判断进行转换,转换为原有真实类型。
    其对类型要求是什么严格的。

    // 已知原有类型进行强制类型转换
    func base02() {
        var num = 1.2345
        pointer := reflect.ValueOf(&num)
        value := reflect.ValueOf(num)
    
        // 转换类型不匹配会直接 panic
        convertPointer := pointer.Interface().(*float64)
        convertValue   := value.Interface().(float64)
    
    
        fmt.Println(convertPointer)       
        fmt.Println(convertValue)         
    }
    
    执行结果:
    0xc000098010
    1.2345
    
    四 、通过反射来遍历一个结构体,其方法,其feild
    1. 先获取interface的reflect.Type,然后通过NumField进行遍历
    2. 再通过reflect.Type的Field获取其Field
    3. 最后通过Field的Interface()得到对应的value
    type User struct {
        Id      int
        Name    string
        Work    Worker
    }
    
    type Worker struct {
        Id           int
        Occupation   string
    }
    
    func (u User) ReflectCallFunc() {
        fmt.Printf("My Id :%d ,My Name :%s ,My Occupation :%s",u.Id,u.Name,u.Work.Occupation)
    }
    
    func (u User) ReflectCallFuncHasArgs(foo int) {
        fmt.Printf("This is number %d \n", foo)
    }
    
    
    // 遍历类型探测其值
    func base03(input interface{}) {
        getType := reflect.TypeOf(input)
        fmt.Println("get type is: ",getType.Name())  // get type is:  User
    
        getValue := reflect.ValueOf(input)
        fmt.Println("get all Fields is ", getValue) // get all Fields is  {1 ZhangSan {2 Programmer}}
    
        // 对字段进行遍历 获取方法的字段
        for i := 0;i < getType.NumField();i++ {
            field := getType.Field(i)
            value := getValue.Field(i).Interface()
            fmt.Printf("%s: %v = %v \n",field.Name,field.Type,value)
        }
    
        /*
            Id: int = 1
            Name: string = ZhangSan
            Work: main.Worker = {2 Programmer}
        */
    
        // 获取方法
        for i := 0;i < getType.NumMethod();i++ {
            m := getType.Method(i)
            fmt.Printf("%s : %v\n",m.Name,m.Type)
        }
    
        /*
            ReflectCallFunc : func(main.User)
            ReflectCallFuncHasArgs : func(main.User, int)
        */
    }
    
    五 、 通过反射来重新赋值

    reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值,即:要修改反射类型的对象就一定要保证其值是“addressable”的。

    // 通过反射设置变量的值
    func base04() {
        var num = 1.234
        fmt.Println("old value of pointer: ",num) // old value of pointer:  1.234
    
        // 获取其指针(必须是指针)
        pointer := reflect.ValueOf(&num)
        newValue := pointer.Elem()
    
        fmt.Println("type of pointer:",newValue.Type()) // type of pointer: float64
        fmt.Println("settability of pointer",newValue.CanSet()) // settability of pointer true
    
        // 重新赋值
        newValue.SetFloat(3.1415)
        fmt.Println("new value of pointer: ",num) // old value of pointer:  1.234
    }
    
    六、通过反射调用方法

    我们可以利用反射来编写灵活的代码
    按名字访问结构成员
    reflect.ValueOf(e).FieldByName("Name")
    按名字访问结构方法
    reflect.ValueOf(
    e).MethodByName("FuncName").Call([]reflect.Value{reflect.ValueOf(1)})

    // 通过反射调用方法
    func base05(input interface{}) {
        getValue := reflect.ValueOf(input)
    
        // 有参数调用
        methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
        args := []reflect.Value{reflect.ValueOf(2)}
        methodValue.Call(args)
    
        // 无参数调用
        methodValue = getValue.MethodByName("ReflectCallFunc")
        args = make([]reflect.Value,0)
        methodValue.Call(args)
    }
    

    七、判断一个参数是不是指针
    kind 可以进行类型判断, 加上 string() 方法,就可以将类型进行字符串打印。

    // 最简单调用
    func base06(input interface{}) {
        k := reflect.TypeOf(input).Kind().String()
        fmt.Println(k)  
    
        t := reflect.TypeOf(input).Elem()
        v := reflect.ValueOf(input).Elem()
        fmt.Println(t)  
        fmt.Println(v)
    }
    
    执行结果
    ptr
    main.User
    {1 ZhangSan {2 Programmer}}
    
    
    八、记一类特殊的反射

    在对json数据的序列化,反序列化的时候,经常会看到如下的代码

    type Config struct {
        Id         int       `bson:"id" json:"id"`
        Created    time.Time `bson:"created" json:"created"`
        Updated    time.Time `bson:"updated" json:"updated"`
        Creator    string    `bson:"creator" json:"creator"`
        Updater    string    `bson:"updater" json:"updater"`
    }
    
    

    struct tag ,在struct field 的后面有一个"format:normal"格式的 tag。其实在解析时,这些东西就是用反射去解析的。
    如下面的示例

    type Employee struct {
        EmployeeID string
        Name       string `format:"normal"`
        Age        int
    }
    
    func base07() {
        e := &Employee{"1", "Mike", 30}
        //按名字获取成员
        fmt.Printf("Name: value(%[1]v), Type(%[1]T) \n", reflect.ValueOf(*e).FieldByName("Name"))
        if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
            fmt.Println("Failed to get 'Name' field.")
        } else {
            // 获取 tag中值
            fmt.Println("Tag:format", nameField.Tag.Get("format"))
        }
    }
    
    执行结果:
    Name: value(Mike), Type(reflect.Value) 
    Tag:format normal
    

    相关文章

      网友评论

          本文标题:go 的反射 reflect

          本文链接:https://www.haomeiwen.com/subject/smhidctx.html