zoukankan      html  css  js  c++  java
  • golang中反射的使用

    将 value 强制转换为已知类型

     我们可以通过 ValueOf 拿到了内存中实际的值,从原理上来说,只要通过强制类型转换,就可以将他转换为我们需要的类型了

    转换为基本类型

    Value 类型绑定了以下几种基本类型的转换方法:

    func (v Value) Bool() bool
    func (v Value) Bytes() []byte
    func (v Value) Int() int64
    func (v Value) Float() float64
    func (v Value) Interface() (i interface{})
    func (v Value) Pointer() uintptr
    func (v Value) Uint() uint64

    如果该类型已知为上述某种基本类型,通过上述方法就可以直接转换为对应的值了

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type temprature int
    
    func main() {
        var temp interface{} = temprature(5)
        fmt.Printf("temprature is %d
    ", temp.(temprature))
        itype := reflect.TypeOf(temp)
        ivalue := reflect.ValueOf(temp)
        fmt.Printf("%s: %d", itype, ivalue.Int())
    }

    转换为任意已知类型

    但如果你想将他转换为其他任意类型呢?也很简单,因为 Value 提供了转换为 interface{} 类型的方法,在这之后,通过类型断言,我们可以轻易将变量转换为我们需要的类型:

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type User struct {
        Id   int
        Name string
        Age  int
    }
    
    func main() {
        var temp interface{} = User{Id: 1, Name: "Nico", Age: 17}
        itype := reflect.TypeOf(temp)
        ivalue := reflect.ValueOf(temp)
        fmt.Printf("%s: %v", itype, ivalue.Interface().(User))
    }

    类型推断

    反射最为常用的场景是在运行时推断类型,从而获取到传递的实际数据:

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type User struct {
        Id   int
        Name string
        Age  int
    }
    
    func (u User) OutputSelf() {
        fmt.Printf("No.%d OutputSelf name: %s, age: %d
    ", u.Id, u.Name, u.Age)
    }
    func main() {
        var temp interface{} = User{Id: 1, Name: "Nico", Age: 17}
        itype := reflect.TypeOf(temp)
        ivalue := reflect.ValueOf(temp)
    
        // 获取字段类型与字段值
        fmt.Println("reflect get fields:")
        for i := 0; i < itype.NumField(); i++ {
            field := itype.Field(i)
            value := ivalue.Field(i).Interface()
            fmt.Printf("%s: %v = %v
    ", field.Name, field.Type, value)
        }
    
        // 获取方法
        fmt.Println("reflect get methods:")
        for i := 0; i < itype.NumMethod(); i++ {
            m := itype.Method(i)
            fmt.Printf("%s: %v
    ", m.Name, m.Type)
        }
    }

    动态设置值

    反射一个非常重要的作用就是动态改变变量的值,从而在运行时实现通用性极强的一些功能

    设置基本类型的值

    设置一个基本类型变量的值是最基本的操作,主要有以下几步:

    1.通过 reflect.ValueOf() 获取变量对应的 Value 对象,需要注意的是,此步骤必须对变量取地址后获取,否则接下来一步将无法设置

    2.通过 Value 对象的 Elem() 方法获取到指针引用的内存变量并设置为可寻址

    3.通过 Elem() 方法返回的 Value 对象的 Set() 方法,我们就可以设置相同类型的值了

    import (
        "fmt"
        "reflect"
    )
    
    func main() {
        iVal := 5
        rVal := reflect.ValueOf(&iVal) // 必须取地址,否则会抛出 panic: reflect: call of reflect.Value.Elem on int Value
        valElem := rVal.Elem()
        valElem.Set(reflect.ValueOf(13)) // Set 传递的参数类型必须与原值类型一致,否则抛出 panic: reflect.Set: value of type string is not assignable to type int
        fmt.Printf("new iVal is: %v
    ", iVal)
    }

    设置 slice 元素或数组元素

    与上述设置基本类型的程序十分类似,只是获取内存地址并设为可寻址的 Elem() 方法改为 Index(i int) 方法

    需要注意的是,在获取目标类型指针对应的 Value 对象时,我们需要区分:

    1.slice 本身持有数组的指针,所以无需通过 & 运算获取地址

    2.对于数组来说,& 运算符获取数组的地址是必须的

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    func main() {
        aVal := []int{1}
        rVal := reflect.ValueOf(aVal) // slice 本身持有数组的指针,所以此处无需取地址
        valElem := rVal.Index(0)
        valElem.Set(reflect.ValueOf(13))
        fmt.Printf("new aVal[0] is: %v
    ", aVal[0])
    }

    设置可寻址的结构体字段

    对于结构体,我们必须要指定需要设置的字段,Value 类型提供了 FieldByName 方法用来实现这个功能

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type Student struct {
        Id int
        Name string
        Score int
    }
    
    func main() {
        student := Student{Id: 1, Name: "Nico", Score: 80}
        rVal := reflect.ValueOf(&student) // 必须取地址,否则会抛出 panic: reflect: call of reflect.Value.Elem on int Value
        valElem := rVal.Elem()
        score := valElem.FieldByName("Score")
        score.SetInt(99)
        fmt.Printf("student is: %v
    ", student)
    }
  • 相关阅读:
    找到一种给vs2012对话框插入背景图片不会失真的方法
    第一次用C语言把数据写入文件中
    Java学习路线图
    一:MyBatis Generator 【SpringMvc+Spring+MyBatis+Maven整合学习笔记】
    windows系统安装Redis
    js子级窗口相互调用父级的方法
    MSSQL 发布订阅,实现读写分离
    查看MS SQL最耗时间资源的SQL
    数据库优化
    mybatis与hibernate区别
  • 原文地址:https://www.cnblogs.com/peteremperor/p/14096459.html
Copyright © 2011-2022 走看看