【GO】一篇文章带你看透反射的原理

反射是什么

  • 对于运行时内存中的任何一个对象,你不需要预先知道其类型是什么,也能访问其全部属性,调用其全部方法;
  • 反射的主要作用在于编写通用的框架;
  • 用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。Zero接受一个Type类型参数并返回一个代表该类型零值的Value类型值。

反射应用场景举例:导出商品列表到Excel

  • 需求是:不管用户在界面上看到什么商品,当它点击一下导出按钮,就将该商品的所有属性和值写出为文件;
  • 本例的难点是:我们无法预知用户会选择导出什么类型的商品数据、它有哪些属性,也就无法相应地去创建Excel数据表的列;
  • 因为商品的种类太多,如果用“正射”去做,那么有多少商品类型我们就要写多少个switch或if分支,然后在每一个分支里根据当前分支的具体商品类型去构造相应的数据列,这显然是狠蹩脚、狠难维护和扩展的;
  • 而通过反射来做就易如反掌了,管你用户要导出的是什么商品实例,我可以动态地解析其类型、动态获知其所有属性和方法,然后根据再根据其具体属性名称去创建相应的表格列,并将属性值填入其中;

然后我们开始看案例

首先需要定义俩个结构体

并且People继承PeopleParent

package main

import (
	"fmt"
	"reflect"
)

type PeopleParent struct {
	Kaka string
}

type People struct {
	PeopleParent
	Name string
	Age  int
}

定义一个方法为了在使用value接口演示使用

func (p People) Eat(name string) {
	fmt.Println("咔咔在吃什么呢!", name)
	p.Name = name
}

在main函数里边我们把People结构体的对象给创建出来

创建了机构对象并且复制给P 并且调用了typeAPI方法,把p传入进去

func main() {
	p := People{
		PeopleParent: PeopleParent{Kaka: "咔咔的父类属性"},
		Name:         "咔咔",
		Age:          24,
	}
	typeAPI(p)
	valueAPI(p)
}

开始我们的typeAPI的一些接口

func typeAPI(obj interface{}) {
	// 返回保存值的类型
	oType := reflect.TypeOf(obj)
	fmt.Println(oType) // main.People
	// 原始类型
	kind := oType.Kind()
	fmt.Println(kind) // struct
	// 类型名称
	fmt.Println(oType.Name()) // People
	// 属性和方法的个数
	fmt.Println(oType.NumField())  // 2
	fmt.Println(oType.NumMethod()) // 0
	// 获取全部属性
	for i := 0; i < oType.NumField(); i++ {
		structField := oType.Field(i)
		// name string    age int
		fmt.Println(structField.Name, structField.Type)
	}
	// 获取全部方法
	for i := 0; i < oType.NumMethod(); i++ {
		structMethod := oType.Method(i)
		fmt.Println(structMethod.Name, structMethod.Type)
	}
	// 获取父类的属性  []int{0, 0}获取第0个父类  第0个属性
	fmt.Println(oType.FieldByIndex([]int{0, 0}).Name)
}

value的一些接口

在这里我们需要注意的是方法传入的是结构体的值并非指针

在修改属性值时是传的指针,这点看清楚

我们看到的是elem也可以调用结构体的方法

我们代码的一开始是ValueOf§ 如果改为*p也是跟elem一样的

	valueOf := reflect.ValueOf(&p)
	byEat := valueOf.MethodByName("Eat")
	byEat.Call([]reflect.Value{reflect.ValueOf("西瓜")})
	fmt.Println(valueOf)
func valueAPI(p People) {
	valueOf := reflect.ValueOf(p)
	// 获取所有属性值
	for i := 0; i < valueOf.NumField(); i++ {
		value := valueOf.Field(i)
		// {}
		//咔咔
		//24
		fmt.Println(value)
	}
	// 获取父类属性
	fieldByIndex := valueOf.FieldByIndex([]int{0, 0})
	fmt.Println(fieldByIndex.Interface()) // 咔咔的父类属性

	// 获得指针value的内容进而获得成员的值
	valuePrt := reflect.ValueOf(&p)
	elem := valuePrt.Elem()
	value := elem.Field(0).Interface()
	fmt.Println(value) //{咔咔的父类属性}

	// 根据属性名获取值
	age := elem.FieldByName("Age")
	fmt.Println("咔咔的年龄", age) // 咔咔的年龄 24
	// 修改属性值
	elem.FieldByName("Age").SetInt(26)
	fmt.Println(elem) //{{咔咔的父类属性} 咔咔 26}

	// 调用对象的方法
	mValue := elem.MethodByName("Eat")
	// 参数需要反射
	mValue.Call([]reflect.Value{reflect.ValueOf("西瓜")})
	fmt.Println(elem) //咔咔在吃什么呢! 西瓜
}

完整代码

package main

import (
	"fmt"
	"reflect"
)

type PeopleParent struct {
	Kaka string
}

type People struct {
	PeopleParent
	Name string
	Age  int
}

func (p People) Eat(name string) {
	fmt.Println("咔咔在吃什么呢!", name)
	p.Name = name
}

func main() {
	p := People{
		PeopleParent: PeopleParent{Kaka: "咔咔的父类属性"},
		Name:         "咔咔",
		Age:          24,
	}
	typeAPI(p)
	valueAPI(p)
}
func typeAPI(obj interface{}) {
	// 返回保存值的类型
	oType := reflect.TypeOf(obj)
	fmt.Println(oType) // main.People
	// 原始类型
	kind := oType.Kind()
	fmt.Println(kind) // struct
	// 类型名称
	fmt.Println(oType.Name()) // People
	// 属性和方法的个数
	fmt.Println(oType.NumField())  // 2
	fmt.Println(oType.NumMethod()) // 0
	// 获取全部属性
	for i := 0; i < oType.NumField(); i++ {
		structField := oType.Field(i)
		// name string    age int
		fmt.Println(structField.Name, structField.Type)
	}
	// 获取全部方法
	for i := 0; i < oType.NumMethod(); i++ {
		structMethod := oType.Method(i)
		fmt.Println(structMethod.Name, structMethod.Type)
	}
	// 获取父类的属性  []int{0, 0}获取第0个父类  第0个属性
	fmt.Println(oType.FieldByIndex([]int{0, 0}).Name)
}

func valueAPI(p People) {
	valueOf := reflect.ValueOf(p)
	//valueOf := reflect.ValueOf(&p)
	//byEat := valueOf.MethodByName("Eat")
	//byEat.Call([]reflect.Value{reflect.ValueOf("西瓜")})
	//fmt.Println(valueOf)
	// 获取所有属性值
	for i := 0; i < valueOf.NumField(); i++ {
		value := valueOf.Field(i)
		// {}
		//咔咔
		//24
		fmt.Println(value)
	}
	// 获取父类属性
	fieldByIndex := valueOf.FieldByIndex([]int{0, 0})
	fmt.Println(fieldByIndex.Interface()) // 咔咔的父类属性

	// 获得指针value的内容进而获得成员的值
	valuePrt := reflect.ValueOf(&p)
	elem := valuePrt.Elem()
	value := elem.Field(0).Interface()
	fmt.Println(value) //{咔咔的父类属性}

	// 根据属性名获取值
	age := elem.FieldByName("Age")
	fmt.Println("咔咔的年龄", age) // 咔咔的年龄 24
	// 修改属性值
	elem.FieldByName("Age").SetInt(26)
	fmt.Println(elem) //{{咔咔的父类属性} 咔咔 26}

	// 调用对象的方法
	mValue := elem.MethodByName("Eat")
	// 参数需要反射
	mValue.Call([]reflect.Value{reflect.ValueOf("西瓜")})
	fmt.Println(elem) //咔咔在吃什么呢! 西瓜
}

博主微信欢迎交流

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咔咔-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值