import “reflect”

reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值,
1、通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。
2、调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。
3、Zero接受一个Type类型参数并返回一个代表该类型零值的Value类型值。

接下来通过几个代码来演示

package main
import (
	"reflect"
	"fmt"
)


//专门演示反射
func reflectTest01(b interface{}) {

	//通过反射获取的传入的变量的 type , kind, 值
	//1. 先获取到 reflect.Type
	rTyp := reflect.TypeOf(b)
	fmt.Println("rType=", rTyp)

	//2. 获取到 reflect.Value
	rVal := reflect.ValueOf(b)
	
	n2 := 2 + rVal.Int()
	//n3 := rVal.Float()
	fmt.Println("n2=", n2)
	//fmt.Println("n3=", n3)
	
	fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal)

	//下面我们将 rVal 转成 interface{}
	iV := rVal.Interface()
	//将 interface{} 通过断言转成需要的类型
	num2 := iV.(int)
	fmt.Println("num2=", num2)


}

//专门演示反射[对结构体的反射]
func reflectTest02(b interface{}) {

	//通过反射获取的传入的变量的 type , kind, 值
	//1. 先获取到 reflect.Type
	rTyp := reflect.TypeOf(b)
	fmt.Println("rType=", rTyp)

	//2. 获取到 reflect.Value
	rVal := reflect.ValueOf(b)

	//3. 获取 变量对应的Kind
	//(1) rVal.Kind() ==> 
	kind1 := rVal.Kind()
	//(2) rTyp.Kind() ==>
	kind2 := rTyp.Kind()
	fmt.Printf("kind =%v kind=%v\n", kind1, kind2)
	


	//下面我们将 rVal 转成 interface{}
	iV := rVal.Interface()
	fmt.Printf("iv=%v iv type=%T \n", iV, iV)
	//将 interface{} 通过断言转成需要的类型
	//这里,我们就简单使用了一带检测的类型断言.
	//同学们可以使用 swtich 的断言形式来做的更加的灵活
	stu, ok := iV.(Student)
	if ok {
		fmt.Printf("stu.Name=%v\n", stu.Name)
	}

}

type Student struct {
	Name string
	Age int
}

type Monster struct {
	Name string
	Age int
}

func main() {

	//请编写一个案例,
	//演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作

	//1. 先定义一个int
	var num int = 100
	reflectTest01(num)

	//2. 定义一个Student的实例
	stu := Student{
		Name : "tom",
		Age : 20,
	}
	reflectTest02(stu)


}

输出

rType= int
n2= 102
rVal=100 rVal type=reflect.Value
num2= 100

///func2
rType= main.Student
kind =struct kind=struct
iv={tom 20} iv type=main.Student 
stu.Name=tom

该代码用普通类型 int和结构体 struct来实验。
函数的参数要用interface{}), 例如func reflectTest01(b interface{}) {…}

都用到了
reflect.TypeOf(b) 、 reflect.ValueOf(b) 和 rVal.Interface()
这几个方法,所以说这几个方法很普遍。
在这里插入图片描述

它们是可以相互转换的

例子2

 
package main
import (
	"reflect"
	"fmt"
)

//通过反射,修改,
// num int 的值
// 修改 student的值

func reflect01(b interface{}) {
	//2. 获取到 reflect.Value
	rVal := reflect.ValueOf(b)
	// 看看 rVal的Kind是 
	fmt.Printf("rVal kind=%v\n", rVal.Kind())
	//3. rVal
	//Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
	rVal.Elem().SetInt(20)
}

func main() {

	var num int = 10
	reflect01(&num)
	fmt.Println("num=", num) // 20


	//你可以这样理解rVal.Elem()
	// num := 9
	// ptr *int = &num
	// num2 := *ptr  //=== 类似 rVal.Elem()
}

输出

rVal kind=ptr
num= 20

主函数传入地址,意味着要修改变量,所以 rVal的 kind是 ptr,但是如果直接用 rVal.SetInt(20) 是会报错的,因为SetInt是 value类型的方法,而不是 *value
这就要用到我们的 Elem()方法了,rVal.Elem(), 类似于取值符号 “ * ”

你可以这样理解 rVal.Elem()
num := 10
var ptr *int = &num
num2 := *ptr //=== 类似 rVal.Elem()

下一个例子是反射的最佳应用(尚硅谷教程)

package main
import (
	"fmt"
	"reflect"
)
//定义了一个Monster结构体
type Monster struct {
	Name  string `json:"name"`
	Age   int `json:"monster_age"`
	Score float32 `json:"成绩"`
	Sex   string
	
}

//方法,返回两个数的和
func (s Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}
//方法, 接收四个值,给s赋值
func (s Monster) Set(name string, age int, score float32, sex string) {
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}

//方法,显示s的值
func (s Monster) Print() {
	fmt.Println("---start~----")
	fmt.Println(s)
	fmt.Println("---end~----")
}
func TestStruct(a interface{}) {
	//获取reflect.Type 类型
	typ := reflect.TypeOf(a)
	//获取reflect.Value 类型
	val := reflect.ValueOf(a)
	//获取到a对应的类别
	kd := val.Kind()
	//如果传入的不是struct,就退出
	if kd !=  reflect.Struct {
		fmt.Println("expect struct")
		return
	}

	//获取到该结构体有几个字段
	num := val.NumField()

	fmt.Printf("struct has %d fields\n", num) //4
	//变量结构体的所有字段
	for i := 0; i < num; i++ {
		fmt.Printf("Field %d: 值为=%v\n", i, val.Field(i))
		//获取到struct标签, 注意需要通过reflect.Type来获取tag标签的值
		tagVal := typ.Field(i).Tag.Get("json")
		//如果该字段于tag标签就显示,否则就不显示
		if tagVal != "" {
			fmt.Printf("Field %d: tag为=%v\n", i, tagVal)
		}
	}
	
	//获取到该结构体有多少个方法
	numOfMethod := val.NumMethod()
	fmt.Printf("struct has %d methods\n", numOfMethod)
	
	//var params []reflect.Value
	//方法的排序默认是按照 函数名的排序(ASCII码)
	val.Method(1).Call(nil) //获取到第二个方法。调用它

	
	//调用结构体的第1个方法Method(0)
	var params []reflect.Value  //声明了 []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(40))
	res := val.Method(0).Call(params) //传入的参数是 []reflect.Value, 返回[]reflect.Value
	fmt.Println("res=", res[0].Int()) //返回结果, 返回的结果是 []reflect.Value*/

}
func main() {
	//创建了一个Monster实例
	var a Monster = Monster{
		Name:  "黄鼠狼精",
		Age:   400,
		Score: 30.8,
	}
	//将Monster实例传递给TestStruct函数
	TestStruct(a)	
}

struct has 4 fields
Field 0: 值为=黄鼠狼精
Field 0: tag为=name
Field 1: 值为=400
Field 1: tag为=monster_age
Field 2: 值为=30.8
Field 2: tag为=成绩
Field 3: 值为=
struct has 3 methods
---start~----
{黄鼠狼精 400 30.8 }
---end~----
res= 50

value.Field(i) 和 type.Field(i) 两者的返回值完换不同
1、在这里插入图片描述
2、在这里插入图片描述
在这里插入图片描述
而 type.field(i)返回类型是StructField,它又是一个结构体
在这里插入图片描述
两个都是对 struct里的变量进行查看
value.Field(i) 返回一个 value类型,只能看一看值,不能对值加减运算。
type.Field(i) 返回 StructField 类型,它又是一个 struct, 里面的 tag 变量尤其重要

type Monster struct {
	Name  string `json:"name"`
	Age   int `json:"monster_age"`

tag 是什么呢,就是这个 “json:” , 用 get方法可以反射获得 name的值。

tagVal := typ.Field(i).Tag.Get("json")

这句代码就可以理解了。

第二个要注意的点是

//方法的排序默认是按照 函数名的排序(ASCII码)
	val.Method(1).Call(nil) //获取到第二个方法。调用它

value的 Method 方法,通过它可以调用 value结构体的方法,它调用的顺序是函数名的ascii排序
GetSum、Set、Print这三个函数的顺序应该是GetSum、Print、Set
故调用第二个,Print

例4

package test

import (
	"reflect"
	"testing"
)

type user struct {
	UserId string
	Name   string
}

func TestReflectStruct(t *testing.T) {
	var (
		model *user //这里是指针
		st    reflect.Type
		elem  reflect.Value
	)
	st = reflect.TypeOf(model)
	t.Log("reflect.TypeOf", st.Kind().String()) //ptr
	st = st.Elem()
	t.Log("reflect.TypeOf.Elem", st.Kind().String()) //struct 取到了值
	//新建一个指向 type的指针
	elem = reflect.New(st)
	t.Log("reflect.New", elem.Kind().String())             //ptr
	t.Log("reflect.New.Elem", elem.Elem().Kind().String()) //struct

	model = elem.Interface().(*user) //model 的值是一个指针,指针指向 usr空间
	elem = elem.Elem()               //elem 现在直接指向了user空间

	elem.FieldByName("UserId").SetString("12345678")
	elem.FieldByName("Name").SetString("xiaopang")
	t.Log("model, model.Name", model, model.Name)
}

=== RUN   TestReflectStruct
    reflect_test.go:20: reflect.TypeOf ptr
    reflect_test.go:22: reflect.TypeOf.Elem struct
    reflect_test.go:25: reflect.New ptr
    reflect_test.go:26: reflect.New.Elem struct
    reflect_test.go:33: model, model.Name &{12345678 xiaopang} xiaopang
--- PASS: TestReflectStruct (0.00s)
PASS

通过反射,可以创建 struct,reflect.New(st)
还可以设置变量的值
elem.FieldByName(“UserId”).SetString(“12345678”)
找值-FieldByName, 设置值-SetString

st = st.Elem()

这个是 type的 elem方法,返回该类型的元素类型,如果该类型的Kind不是Array、Chan、Map、Ptr或Slice,会panic
也就是 *st,指针变成真实类型 struct

elem = reflect.New(st)

在这里插入图片描述
从输出可以看到,它和 st (也就是 ptr type)是一样的,它的类型是 reflect.Value,它指向的值是 reflect.Type

之后新建了一个 model变量,它是指向 user的指针,而 elem也是指向 user(疑问,是指向 type,但是它还是指向 user)。