前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何使用 golang 的反射机制让你事半功倍

如何使用 golang 的反射机制让你事半功倍

作者头像
用户3147702
发布2022-06-27 14:17:49
5440
发布2022-06-27 14:17:49
举报
文章被收录于专栏:小脑斧科技博客

1. 引言

上一篇文章中,我们详细了解了 golang 中反射机制的实现原理。 golang 中的反射(上) — 反射的原理与实现

本文,我们就来详细介绍 golang 中反射的使用。

2. 将 value 强制转换为已知类型

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

2.1. 转换为基本类型

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

代码语言:javascript
复制
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

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

代码语言:javascript
复制
package main

import (
    "fmt"
    "reflect"
)

type temprature int

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

2.2. 转换为任意已知类型

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

代码语言:javascript
复制
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))
}

打印出了:

main.User: {1 Nico 17}

3. 类型推断

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

代码语言:javascript
复制
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\n", 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\n", 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\n", m.Name, m.Type)
    }
}

打印出了:

reflect get fields: Id: int = 1 Name: string = Nico Age: int = 17 reflect get methods: OutputSelf: func(main.User)

4. 动态设置值

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

4.1. 设置基本类型

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

  1. 通过 reflect.ValueOf() 获取变量对应的 Value 对象,需要注意的是,此步骤必须对变量取地址后获取,否则接下来一步将无法设置
  2. 通过 Value 对象的 Elem() 方法获取到指针引用的内存变量并设置为可寻址
  3. 通过 Elem() 方法返回的 Value 对象的 Set() 方法,我们就可以设置相同类型的值了
代码语言:javascript
复制
package main

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\n", iVal)
}

打印出了:

new iVal is: 13

4.2. 设置 slice 元素或数组元素

与上述设置基本类型的程序十分类似,只是获取内存地址并设为可寻址的 Elem() 方法改为 Index(i int) 方法。 需要注意的是,在获取目标类型指针对应的 Value 对象时,我们需要区分:

  • slice 本身持有数组的指针,所以无需通过 & 运算获取地址
  • 对于数组来说,& 运算符获取数组的地址是必须的
代码语言:javascript
复制
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\n", aVal[0])
}

打印出了:

new aVal[0] is: 13

4.3. 设置可寻址的结构体字段

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

代码语言:javascript
复制
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\n", student)
}

打印出了:

student is: {1 Nico 99}

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小脑斧科技博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 引言
  • 2. 将 value 强制转换为已知类型
    • 2.1. 转换为基本类型
      • 2.2. 转换为任意已知类型
      • 3. 类型推断
      • 4. 动态设置值
        • 4.1. 设置基本类型
          • 4.2. 设置 slice 元素或数组元素
            • 4.3. 设置可寻址的结构体字段
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档