reflect反射机制

1 概述  

        reflect 实现运行时反射,允许程序使用任意类型操作对象。典型的用法是用静态类型接口{}取值并通过调用返回类型的TypeOf 来提取其动态类型信息。反射是程序执行时检查其所拥有的结构。

反射在程序中应用非常多,例如:

  • 动态生成数据:json 序列化/反序列化; orm 映射, proxy 透明代理对象
  • 动态调用方法:plugin 实现
  • 框架自动处理程序:annotation tag 注解标签
  • 其他需要数据元数据的应用

在必要的场合,灵活应用反射,是中高级程序员能力的评价标准之一。灵活应用的根本是加深对 go 语言编译与实现的理解,并阅读典型应用案例。


2 反射的三大规则

     1 反射是从接口值到反射对象 

 变量/数据对象 – 反射 -> 值,具体类型
            reflect 包中的两种类型: Type 和 Value 
            Kind 方法: Kind 是 go 基础类型的枚举
            重要:反射是编译将值转为接口,TypeOf,ValueOf 只是简单取出接口值的内容 
                      反射是编译期决定的扩展,go 运行期仅加载必要的元数据
                      根据接口的表示,反射对象类型不可能是接口

            go 反射的本质 
                      数据对象 – 编译 -> 接口(值,具体类型)– 反射 -> 值,具体类型

     2 从反射对象可反射出接口值 

             值可反射回接口值:   func (v Value) Interface() interface{}

             特别注意 v 和 v.Interface() 的区别:

                    1 v.Interface() 是反射的原始对象

                    2 v 是原始对象的 reflect.Value 值

     3 要修改反射对象,其值必须可设置 

            reflect.Value 是 原始对象值的 copy 是不可变的

           要修改原始对象需要传地址: v.Elem() 返回指针内容或接口值,它是可修改的


3 反射对结构体的操作

  •  必须传地址才能修改 struct 中的字段
  • 获取结构体 Fields (仅可导出的) 

       v.NumField() 获得结构体 Field 数量
       v.Field(i int) 获取 Field 的值
       v.FieldByXXX(...)

  • 获取结构体字段的 tag 

       type StructField
       StructTag 的文字表达规范:key:”value”
 


4 反射使用实例

  • 反射中调用函数
func helloPrint(i int) int {
	fmt.Println("i=", i)
	return i * i
}

func TestHelloPrint(t *testing.T) {
	fv := reflect.ValueOf(helloPrint)
	params := make([]reflect.Value, 1)              //参数
	params[0] = reflect.ValueOf(11)                 //参数
	rs := fv.Call(params)                           //rs作为结果接受函数的返回值
	fmt.Println("result:", rs[0].Interface().(int)) //当然也可以直接是rs[0].Interface()
}

输出:
i= 11
result: 121
PASS
ok  	_/g_/goproject/src/helloworld	0.273s
Success: Tests passed.
  • 反射中调用方法

上面说了在反射中调用函数的例子,接下来我们要谈谈反射中方法的调用。函数和方法可以说其实本质上是相同的,只不过方法与一个“对象”进行了“绑定”,方法是“对象”的一种行为,这种行为是对于这个“对象”的一系列操作,例如修改“对象”的某个属性

type MyType struct {
	i    int
	name string
}

func (mt *MyType) SetI(i int) {
	mt.i = i
}

func (mt *MyType) SetName(name string) {
	mt.name = name
}

func (mt *MyType) String() string {
	return fmt.Sprintf("%p", mt) + "--name:" + mt.name + " i:" + strconv.Itoa(mt.i)
}

func TestStructMethod(t *testing.T) {
	myType := &MyType{22, "jking"}
	mtV := reflect.ValueOf(&myType).Elem()
	fmt.Println("Before:", mtV.MethodByName("String").Call(nil)[0])
	params := make([]reflect.Value, 1)
	params[0] = reflect.ValueOf(18)
	mtV.MethodByName("SetI").Call(params)
	params[0] = reflect.ValueOf("reflection test")
	mtV.MethodByName("SetName").Call(params)
	fmt.Println("After:", mtV.MethodByName("String").Call(nil)[0])
}

func TestStructMethod1(t *testing.T) {
	myType := &MyType{22, "jking"}
	// mtV := reflect.ValueOf(&myType).Elem()
	// fmt.Println("Before:", mtV.MethodByName("String").Call(nil)[0])
	// params := make([]reflect.Value, 1)
	// params[0] = reflect.ValueOf(18)
	// mtV.MethodByName("SetI").Call(params)
	// params[0] = reflect.ValueOf("reflection test")
	// mtV.MethodByName("SetName").Call(params)
	// fmt.Println("After:", mtV.MethodByName("String").Call(nil)[0])

	mtV := reflect.ValueOf(&myType).Elem()
	fmt.Println("Before:", mtV.Method(2).Call(nil)[0])
	params := make([]reflect.Value, 1)
	params[0] = reflect.ValueOf(18)
	mtV.Method(0).Call(params)
	params[0] = reflect.ValueOf("reflection test")
	mtV.Method(1).Call(params)
	fmt.Println("After:", mtV.Method(2).Call(nil)[0])
	//其中对象的方法以函数名排列, Method(idx) idx为函数的排列后数据的标识
}


输出:
Before: 0xc042048420--name:jking i:22
After: 0xc042048420--name:reflection test i:18
PASS
ok  	_/g_/goproject/src/helloworld	0.159s
Success: Tests passed.
  • 反射在结构体在的调用
type user struct {
	UserId string `model:"pk" type:"string"`
	Name   string
	Lvl    int
}

func TestReflectFunc(t *testing.T) {
	call1 := func(v1 int, v2 int) {
		fmt.Println("i1:=", v1)
		fmt.Println("i2:=", v2)
	}

	var (
		function reflect.Value
		inValue  []reflect.Value
		n        int
	)
	bridge := func(call interface{}, args ...interface{}) {
		n = len(args)
		inValue = make([]reflect.Value, n)
		for i := 0; i < n; i++ {
			inValue[i] = reflect.ValueOf(args[i])
		}
		function = reflect.ValueOf(call)
		function.Call(inValue)
	}

	bridge(call1, 1, 2)
}

更多的原理与使用方法参考:https://www.cnblogs.com/susufufu/archive/2017/10/12/7653579.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值