Golang基础笔记十六之反射

本文首发于公众号:Hunter后端

原文链接:Golang基础笔记十六之反射

反射可以用于程序在运行时检查、修改自身类型和值,主要通过 reflect 包实现。

首先,我们提出一个需求,要打印出一个结构体 struct 的各个字段及其对应的标签数据,按照当前的笔记内容是无法解决该问题的,但是我们可以使用反射操作来完成。

以下是本篇笔记目录:

  1. 变量的类型和值
  2. 修改变量的值
  3. 遍历结构体字段
  4. 动态调用函数

1、变量的类型和值

先引入 reflect 模块:

go 复制代码
import (
    "reflect"
)

我们可以通过 reflect.TypeOf() 获取变量的类型:

go 复制代码
var x float64 = 3.5

t := reflect.TypeOf(x)

返回的 t 是 Type 接口,我们可以进一步调用 t 的方法来获取类型信息:

go 复制代码
// 变量的类型名称:
fmt.Println("x 的类型名称是: ", t.Name())

// 判断类型的类别:
fmt.Println("x 的类型是否是 float64: ", t.Kind() == reflect.Float64)

获取变量的值信息:

go 复制代码
v := reflect.ValueOf(x)
fmt.Println("value: ", v.Float() == 3.5)

2、修改变量的值

如果要修改这个变量的值,我们需要用到指针,以下是操作示例:

go 复制代码
var x float64 = 3.5

// 这里获取的是变量的地址的值,如果直接 reflect.ValueOf(x) 获取的是 x 的副本
p := reflect.ValueOf(&x)

// Elem() 方法获取指针指向的实际值,是解引用的操作
v := p.Elem()

// 重新赋值的操作
v.SetFloat(4.9)
fmt.Println("new value: ", x)

3、遍历结构体字段

我们先定义一个结构体如下:

go 复制代码
type Person struct {
	Id   int    `json:"id" form:"id"`
	Name string `json:"name"`
}

打印一个 Person 示例各个字段的名称及其值的操作如下:

go 复制代码
p := Person{
    Id:   1,
    Name: "hunter",
}
t := reflect.TypeOf(p)
v := reflect.ValueOf(p)

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)

    fmt.Printf("field_name:%s, field_type:%s, value:%v\n", field.Name, field.Type, value.Interface())
}

在这里,我们通过 t.NumField() 方法获取到 p 的字段个数,并使用 t.Field(i)v.Field(i) 获取到对应字段类型和值。

接着对于每个 field 和 value,我们可以打印出对应的字段名称,字段类型和值。

我们还可以使用 field.Tag.Get() 的方式获取到字段标签的值:

go 复制代码
fmt.Printf("json_tag:%s, form_tag:%s\n", field.Tag.Get("json"), field.Tag.Get("form"))

如果后续我们介绍 Golang 的 validator 模块,可以了解到,validator 就是通过 struct 定义的标签使用反射来对字段值进行验证的。

4、动态调用函数

我们还可以使用反射来动态调用函数,比如某个函数如下:

go 复制代码
func Add(a, b int) int {
    return a + b
}

使用反射动态调用的操作如下:

go 复制代码
func main() {
    targetFunc := reflect.ValueOf(Add)

    args := []reflect.Value{reflect.ValueOf(2), reflect.ValueOf(5)}

    result := targetFunc.Call(args)
    fmt.Println("动态调用 Add 函数,result: ", result[0].Int())
}

注意:虽然反射可以为我们提供一些便利的操作,但是代码的可读性和可维护性会降低,且会降低性能,需要在实际生产中谨慎使用。

相关推荐
XHunter4 天前
Golang基础笔记十五之sync
golang基础笔记
XHunter9 天前
Golang基础笔记十四之文件操作
golang基础笔记
XHunter11 天前
Golang基础笔记十三之context
golang基础笔记
XHunter16 天前
Golang基础笔记十二之defer、panic、error
golang基础笔记
XHunter18 天前
Golang基础笔记十一之日期与时间处理
golang基础笔记
XHunter23 天前
Golang基础笔记十之goroutine和channel
golang基础笔记
XHunter25 天前
Golang基础笔记九之方法与接口
golang基础笔记
XHunter1 个月前
Golang基础笔记三之数组和切片
golang基础笔记
XHunter1 个月前
Golang基础笔记二之字符串及其操作
golang基础笔记