Go 反射

反射

Go 提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作,但是在编译时并不知道这些变量的具体类型。
用途:

  1. 设计一类并不满足普通公共接口的类型的值
  2. 它们没有明确的表示方式
  3. 设计该函数时这些类型还不存在

reflect.Type和reflect.Value

reflect.Type和reflect.TypeOf()

函数reflect.TypeOf接收任意的interface{}类型,并返回对应动态类型的reflect.Type:

t := reflect.TypeOf(3) // 得到一个reflect.Type: 动态类型接口值,返回具体的类型
fmt.Println(t.String()) // "int"
fmt.Println(t)          // 与上面一样"int"

由于打印动态类型值对于调试和日志时有帮助的,所以为fmt.Printf()提供了一个简单的%T标志参数,内部使用reflect.TypeOf的结果输出。

fmt.Printf("%T\n", 3) // "int"

reflect.Value和reflect.ValueOf()

一个reflect.Value可以持有一个任意类型的值,reflect.ValueOf函数接受任意的interface{}类型,并返回对应动态类型的reflect.Value.

v := reflect.ValueOf(3) // 得到一个reflect.Value
fmt.Println(v)          // "3"
fmt.Printf("%v\n", v)   // "3"
fmt.Println(v.String()) // NOTE: "<int value>"

reflect.Value满足fmt.Stringer接口,使用fmt包的标志%v参数,可以将reflect.Value格式化

t := v.Type().           // reflect.Type
fmt.Println(t.String())  // "int"

逆操作

逆操作时调用reflect.ValueOf对应的reflect.Value.Interface方法,它返回一个interface类型,表示reflect.Value对应类型的具体值

v := reflect.ValueOf(3)  // 一个接口值
x := v.Interface()       // 一个interface{}
i := x.(int)             // an int
fmt.Printf("%d\n", i)    // "3"

通过reflect.Value修改值

有一些reflect.Value是可取地址的,我们可以调用reflect.Value的CanAddr方法判断其是否可以背取地址

x := 2
a := reflect.ValueOf(2)    // 2的拷贝
b := reflect.ValueOf(x)     
c := reflect.ValueOf(&x)  // &x的拷贝
d := c.Elem()                  // 解引用方式生成

fmt.Println(a.CanAddr())       // "false"
...
fmt.Println(c.CanAddr()).     // "true"
  1. 所有通过reflect.ValueOf(x)返回的reflect.Value都是不可取地址的
  2. 通过间接获取的reflect.Value都是可取地址的,即使开始是一个不可取地址的Value

通过可取地址的reflect.Value访问变量:

  • 调用Addr(),返回Value,里面包括指向变量的指针。
  • 在Value上调用Interface()方法,返回interface{},里面包括指向变量的指针。
  • 使用类型的断言得到interface类型的接口强制转换为普通的指针
x := 2
d := reflect.ValueOf(&x).Elem()		// 可取地址的Value
px := d.Addr().Interface().(*int)    // px := &x
*px = 3								// x = 3
fmt.Println(x)						// "3"

不使用指针,通过调用可取地址的reflect.Value的reflect.Set方法来更新:

d.Set(reflect.ValueOf(4))
fmt.Println(x) 		// "4"

报错的情况

d.Set(reflect.ValueOf(int64(5)))		// panic:int64 is not assignable to int 对应类型不匹配

x := 2
b := reflect.ValueOf(x)
b.Set(reflect.ValueOf(3) 				// panic: Set using unaddressable value

获取结构体字段标识

对于一个Web服务,大部分Http请求需要将请求中的参数保存到本地变量中,下面示例通过params.Unpack,通过使用结构体成员标签让Http处理函数解析请求参数。

总结

import (
	"fmt"
	"reflect"
)

type S struct {
	Name string `Name:"name"`
	Id int	`Id:"ID"`
}
func main(){
	s := S{
		Name: "emmmmm...",
		Id: 1,
	}
	...
}

使用reflect一般分成三步:

  1. 要去反射的是一个类型的值(这些值都实现了空interface),首先需要把它转化成reflect对象(reflect.Type或者reflect.Value【根据不同的情况调用不同的函数】)
// *reflect.rtype
// 需要传如可取地址而不是拷贝值
t := reflect.TypeOf(&s)	// 得到类型的元数据,通过t可以取得类型定义里面的所有元素
// reflect.Value
// 需要传入可取地址而不是拷贝值
v := reflect.ValueOf(&s)	// 得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
/*
	main.S --类型信息
	{emmmmm... 1} --值信息
*/
  1. 转化为reflect对象之后,可以进行进一步操作,也就是将reflect对象转化为相应的值
tag := t.Elem().Filed(0).Tag	// 获取定义在struct里面的标签
name := v.Elem().Filed(0).String // 获取存储在第一个字段里面的值
/*
	tag: Name:"name"
	name: emmmm...
*/
  1. 通过返回值能返回相应的类型数值
var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println("type", v.Elem().Type())
fmt.Println("kind is float64:", v.Elem().Kind() == reflect.Float64)
fmt.Println("value:", v.Elem().Float())
/*
	type: float64
	kind is float64: true
	value: 3.4
*/

通过反射修改某个值

var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)
/*
	x: 7.1
*/

全部代码:

package main

import (
	"fmt"
	"reflect"
)

type S struct {
	Name string `Name:"name"`
	Id int	`Id:"ID"`
}


func main(){
	s := S{
		Name: "emmmmm...",
		Id: 1,
	}
	// 需要传可取地址
	t := reflect.TypeOf(&s)
	v := reflect.ValueOf(&s)
	fmt.Println(t)
	fmt.Println(reflect.TypeOf(t))
	fmt.Println(v)
	fmt.Println(reflect.TypeOf(v))

	tag := t.Elem().Field(0).Tag
	name := v.Elem().Field(0).String()
	fmt.Printf("Tag: %s, value: %s \n", tag, name)

	var x float64 = 3.4
	vv := reflect.ValueOf(&x)
	// 返回reflect.Value的类型
	fmt.Println("type:", vv.Elem().Type())
	// 对类型进行推断
	fmt.Println("kind is float64:", vv.Elem().Kind() == reflect.Float64)
	// 获取相应的值
	fmt.Println("value:", vv.Elem().Float())

	Ev := vv.Elem()
	Ev.SetFloat(7.1)
	fmt.Println("x:", x)
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值