【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: "咔咔的父类属性"},
	}
	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, 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, age) // 咔咔的年龄 24
	// 修改属性值
	elem.FieldByName("Age").SetInt(26)
	fmt.Println(elem) //{{咔咔的父类属性} 咔咔 26}

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

博主微信欢迎交流

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐