【go】反射机制reflect

目录

前言

1. 导出函数

2.常用操作

2.1 获取&修改值Value

2.1.1 获取

2.1.2 修改

2.2 获取结构体字段

2.3 获取&调用方法


前言

反射机制是运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作,而不需要在编译时就知道这些变量的具体类型。跟Java一样,go也提供了反射功能。

其实之前就已经用过反射机制,如下语句输出变量的动态类型,底层就是用的reflect包实现的。

fmt.Printf("%T\n", i)

所有变量都可以由interface{}变量引用,很多函数或方法参数就是interface{},在函数内部通常需要获取interface{} x接口值的类型,通过判断其类型执行不同的处理逻辑。go接口文章中有谈到接口值包括一个具体的类型和那个类型的,即动态类型和动态值,此处反射方法可以获取其动态类型和动态值。

  • 1.反射可以从接口值到反射类型对象
  • 2.反射可以从反射类型对象到接口值
  • 3.修改反射类型变量的内部值需要保证其可设置性

1. 导出函数

函数 功能
reflect.ValueOf(obj)

返回obj的Value

(如果要对obj进行修改的话传入变量地址)

reflect.TypeOf(obj) 返回obj的对象类型
reflect.Indirect(value) 返回Value,获取了该指针指向的值
reflect.New(typ Type)

返回一个新的type类型的对象

t := reflect.TypeOf(3)  // 获取动态类型,返回reflect.Type
v := reflect.ValueOf(3)    // 获取动态值,返回reflect.Value

reflect包包含一个接口Type和一个结构体Value常用于这种场景,可以通过这两个获取底层字段、调用方法、修改字段等等功能,能够实现Java反射机制的类似功能。Value组合了rtype,rtype实现了Type,所以Value包含了Type的所有方法。

【go】反射机制reflect_第1张图片【go】反射机制reflect_第2张图片

reflect.Value能装载任意的值,还有很多方法来检查其内容,设置其内容。reflect.Value类型的变量和interface{}接口值可以互转。

reflect.Value v可以通过Interface() x方法返回接口值,接口值通过reflect.ValueOf(x)获取其Value。

Value的方法 功能
Kind() Kind 数据是什么类型:slice切面.Struct对象
Type() Type 返回动态类型
Elem() Type  元素类型
Interface() (i interface{}) Value类型转interface{}接口值
CanAddr() 能活获取地址
CanSet() bool 返回值能否更改
Set(x Value)  设置值
NumField() int 结构体字段的个数
FieldByName("a") Value 根据字段名称获取
Field(1) Value 字段下标
NumMethod() int 方法个数
Method(index int) Value 第i个方法
MethodByName(name string) Value 按照名称获取方法
Call(in []Value) []Value 调用当前Value(必须是func类型),参数是in slice,返回[]Value

2.常用操作

通常需要获取值、修改值、获取字段、修改字段、获取方法、调用方法。

2.1 获取&修改值Value

2.1.1 获取

// 获取值
c := reflect.ValueOf(&x) // &x      *int    no 获取指针的值Value
d := c.Elem()            // 2       int     yes (x) 
fmt.Println(d.CanAddr()) // "true" 指针类型可取地址

2.1.2 修改

有一些reflect.Value是可取地址的,如reflect.ValueOf(&x).Elem()来获取任意变量x对应的可取地址的Value,修改Value有两种方式:

  1. 可以通过获取地址转换变量指针而修改值
  2. 通过调用Value的Set方法修改值
// 1.通过指针修改值
d := reflect.ValueOf(&x).Elem()   // d 指向 x
px := d.Addr().Interface().(*int) // px := &x 转化成指向x的指针*int 涉及到Value到interface{}
*px = 3                           // x = 3    // 指针
fmt.Println(x)                    // "3"

// 2.通过可取地址Value的set函数
d.Set(reflect.ValueOf(4))
fmt.Println(x) // "4"

修改值Value有如下限制:

  1. 可赋值性约束:将在运行时执行和编译时进行的可赋值性约束检查。赋值两边的类型不一致,那么程序将抛出一个panic异常;
  2. 可取地址:对一个不可取地址的reflect.Value调用Set方法也会导致panic异常;只有指针类型可以取地址
x := 2
b := reflect.ValueOf(x)
b.Set(reflect.ValueOf(3)) // panic: 不可取地址Value不能调用Set方法

2.2 获取结构体字段

类似于Java获取对象的字段

// 测试用结构体
type stut struct {
	a int
	b int
}

// 获取结构体字段
stru := stut{a:3,b:4}	// 创建结构体实例
struv := reflect.ValueOf(&stru).Elem()  // 指针
a := struv.FieldByName("a")    // 获取结构体a字段
fmt.Printf("%T", a)
fmt.Println(a)

获取到字段后也是Value类型,所有的反射类型都是Value的,可以通过上一节的方式,修改字段的值。

2.3 获取&调用方法

类似于Java遍历类型的方法,还可以调用。go通过Value可以获取Method

    v := reflect.ValueOf(x)
	for i := 0; i < v.NumMethod(); i++ {
		methType := v.Method(i).Type()
		fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
			strings.TrimPrefix(methType.String(), "func"))
	}

使用reflect.Value.Call方法(我们之类没有演示),将可以调用一个Func类型的Value

ValueOf(p).Method(0).Call(nil)    // 获取p类型的第0个方法,并调用,输入参数为空

2.4 获取Tags

类似于Java可以通过反射获取注解annotation,Go可以通过反射获取类型的tag,tag是什么呢?如下,tag就是`http:"1"`这样的,类似于Java注解的@tagxxx,reflect.StructTag类型就是对应这些tag。

tag格式:

  •   由多个部分连接而成,空格分割:`form:"certificate_code" binding:"required,zh_idcard_no"`
  •   部分格式为 key:value
  •   key是非空的字符串,由非控制字符组成,并且不可以是空格、双引号、冒号
  •   value由双引号包围,遵循Go字符串字面值语法
    var data struct {
        Labels     []string `http:"l"`
        MaxResults int      `http:"max"`
        Exact      bool     `http:"x"`
    }

fieldInfo := v.Type().Field(i) // 获取结构体的字段
tag := fieldInfo.Tag           // 获取结构体的tag
name := tag.Get("http")        // 获取该字段 tag部分key为http的值

这个功能最常用地是定义vo类型,解析http请求等等。

你可能感兴趣的:(go,go,golang,反射)