go-反射

go-反射

基本介绍

  1. 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
  2. 若是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
  3. 通过反射,可以修改变量的值,可以调用关联方法。
  4. 使用反射,需要import("reflect")
  5. reflect.TypeOf(变量名) 获取变量的类型,返回reflect.Type类型
  6. reflect.ValueOf(变量名) 获取变量的值,返回reflect.Value类型
  7. reflect.Value是一个结构体类型,通过reflect.Value可以获取关于该变量的很多信息
  8. 反射主要可用于序列化和反序列化
  9. 变量、interface{}和reflect.Value是可以相互转换的

image-20220512204311587

快速入门

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	testReflectBasic(20.5)
	person := Person{"tom", 11} // rType: float64    kind: float64   rVal: 20.5
	testReflectStruct(person)   // rType: main.Person   kind: struct
}

func testReflectBasic(temp interface{}) {
	// 1. 获取reflect.Type
	rType := reflect.TypeOf(temp)
	fmt.Printf("rType: %v\n", rType)
	// 2. 调用kind
	kind := rType.Kind()
	fmt.Printf("kind: %v\n", kind)
	// 3. 获取reflect.Value
	rVal := reflect.ValueOf(temp)
	fmt.Printf("rVal: %v\n", rVal)
	fmt.Printf("Type of rVal: %T\n", rVal) // Type of rVal: reflect.Value
	n1 := rVal.Float()
	fmt.Printf("n1: %v    type: %T\n", n1, n1) // n1: 20.5    type: float64

	// 将rVal转换为interfac{}  在使用断言
	nums := rVal.Interface().(float64)
	fmt.Printf("nums: %v  type: %T\n", nums, nums) // nums: 20.5  type: float64
}

func testReflectStruct(temp interface{}) {
	// 1. 获取reflect.Type
	rType := reflect.TypeOf(temp)
	fmt.Printf("rType: %v\n", rType)
	// 2. 调用kind
	kind := rType.Kind()
	fmt.Printf("kind: %v\n", kind)

	// 3. 获取reflect.Value
	rVal := reflect.ValueOf(temp)
	// 4. 查找字段
	nameField := rVal.FieldByName("Name")
	fmt.Printf("nameField: %v\n", nameField) // nameField: tom

	// 5. 转换为interface{} 并断言
	person := rVal.Interface().(Person)
	fmt.Printf("person: %v\n", person) // person: {tom 11}
	person.Name = "jack"
}

注意事项和细节

  1. reflect.Value.Kind,获取变量的类别,返回的是一个常量

  2. Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的

    1. var num int = 20 num 的 Type是int, Kind也是int
    2. var stu Student stu 的 Type是包名.Student, Kind是struct
  3. 通过反射可以让变量在interface{}和reflect.Value之间相互转换

    struct --> interface --> reflect.Vaue --> interface{} --> struct

  4. 使用反射的方式来获取变量的值,要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(),而不嫩他用其他的,否则panic

  5. 通过反射的来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量值,同时需要使用reflect.Value.Elem()方法

  6. reflect.Value.Elem()

type Student struct {
	Name string
	Age  int
}

func main() {
	stu := Student{"tom", 22}
	test(&stu)
	fmt.Printf("stu: %v\n", stu) // stu: {jack 22}
}

func test(temp interface{}) {
	rVal := reflect.ValueOf(temp)
	fmt.Printf("rVal: %v\n", rVal)        // rVal: &{tom 22}
	fmt.Printf("rVal type : %T\n", rVal)  // rVal type : reflect.Value
	fmt.Printf("rVal: %v\n", rVal.Kind()) // rVal: ptr 指针类型

	fmt.Printf("rVal.Elem(): %v\n", rVal.Elem()) // rVal.Elem(): {tom 22}  结构体

	rVal.Elem().FieldByName("Name").SetString("jack") // 改变Name

	// 断言无法改变指针指向的结构体
	stuVal := rVal.Elem()
	fmt.Printf("stuVal: %v\n", stuVal) // stuVal: {jack 22}
	fmt.Printf("stuVal: %T\n", stuVal) // stuVal: reflect.Value
	// 转换 断言
	stu := stuVal.Interface().(Student)
	stu.Age = 33

}

案例

案例1

// 定义一个结构体
type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Score float64
	Sex   string
}

// 为结构体绑定方法Print
func (this Monster) Print() {
	fmt.Println("-------- Print start -------")
	fmt.Printf("this: %v\n", this)
	fmt.Println("-------- Print end -------")
}

// 为结构体绑定方法GetSum
func (this Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}

// 为结构体绑定方法Set
func (this Monster) Set(name string, age int, score float64, sex string) {
	this.Name = name
	this.Age = age
	this.Score = score
	this.Sex = sex
}

// 测试
func TestStuct(test interface{}) {
	// 获取reflect.Type
	rType := reflect.TypeOf(test)
	// 获取reflect.Value
	rVal := reflect.ValueOf(test)
	// 字段类别
	kind := rVal.Kind()
	fmt.Printf("rType: %v\n", rType) // rType: main.Monster
	fmt.Printf("rVal: %v\n", rVal)   // rVal: {tom 22 99.3 nan}
	fmt.Printf("kind: %v\n", kind)   // kind: struct

	// 判断类别是否是reflect.Struct
	if kind != reflect.Struct {
		fmt.Println("expect struct")
		return
	}

	// 获取结构体字段个数
	numField := rVal.NumField()
	fmt.Printf("numField: %v\n", numField) // numField: 4

	// 依次遍历每个字段
	for i := 0; i < numField; i++ {
		fmt.Printf("rVal.Field(%v): %v\n", i, rVal.Field(i)) // 获取字段的值,类型仍是reflect.Value
		// 获取struct标签,需要通过reflect.Type来获取Tag标签
		// Type 是一个interface, 其Field是一个结构体,其中包含了Tag
		tagVal := rType.Field(i).Tag.Get("json")
		if tagVal != "" {
			fmt.Printf("tagVal: %v\n", tagVal)
		}
	}
	// 根据Tag修改字段值
	ageTag := rType.Field(1).Tag.Get("json")
	fmt.Printf("ageTag: %T\n", ageTag) // ageTag: string

	// 获取结构体方法个数
	numMethod := rVal.NumMethod()
	fmt.Printf("numMethod: %v\n", numMethod) // numMethod: 3

	// 方法 并 调用
	// 方法排序规则是依据方法名的ASCII码进行排序
	rVal.Method(1).Call(nil)

	// 调用结构体第一个方法并传参
	// 反射调用时,Call() 中需要传递的是reflect.Value类型的切片
	var params []reflect.Value
	params = append(params, reflect.ValueOf(10), reflect.ValueOf(20))
	// 反射调用后,返回的值仍是reflect.Value类型的切片
	res := rVal.Method(0).Call(params)
	fmt.Printf("res: %v\n", res)    // res: [<int Value>]
	fmt.Printf("res: %v\n", res[0]) // res: 30
}

func main() {
	monster := Monster{"tom", 22, 99.3, "nan"}
	TestStuct(monster)
}

案例2

func main() {
	monster := Monster{"tom", 22, 99.3, "nan"}
	fmt.Printf("monster: %v\n", monster) // monster: {tom 22 99.3 nan}

	TestStuct(&monster)

	fmt.Printf("monster: %v\n", monster) // monster: {jack 22 99.3 nan}
}

// 定义一个结构体
type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Score float64
	Sex   string
}

// 为结构体绑定方法Print
func (this Monster) Print() {
	fmt.Println("-------- Print start -------")
	fmt.Printf("this: %v\n", this)
	fmt.Println("-------- Print end -------")
}

// 为结构体绑定方法GetSum
func (this Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}

// 为结构体绑定方法Set
func (this Monster) Set(name string, age int, score float64, sex string) {
	this.Name = name
	this.Age = age
	this.Score = score
	this.Sex = sex
}

// 测试
func TestStuct(test interface{}) {
	rVal := reflect.ValueOf(test)
	fmt.Printf("rVal: %v\n", rVal)

	// 字段
	numField := rVal.Elem().NumField()
	fmt.Printf("numField: %v\n", numField) //numField: 4

	rVal.Elem().Field(0).SetString("jack")

	// 方法
	numMethod := rVal.NumMethod()
	fmt.Printf("numMethod: %v\n", numMethod) // numMethod: 3

	parameters := []reflect.Value{reflect.ValueOf(30), reflect.ValueOf(50)}
	res := rVal.Elem().Method(0).Call(parameters)
	fmt.Printf("res: %v\n", res)    // res: [<int Value>]
	fmt.Printf("res: %v\n", res[0]) // res: 80

}

案例3

func main() {
	cal := Cal{30, 20}
	testCal(cal)
}

type Cal struct {
	Num1 int
	Num2 int
}

func (this Cal) GetSub(name string) {
	fmt.Println(name, " import getSub: ", this.Num1-this.Num2)
}

func testCal(test interface{}) {
	rType := reflect.TypeOf(test)
	rVal := reflect.ValueOf(test)
	numField := rVal.NumField()
	numMethod := rVal.NumMethod()
	fmt.Printf("numField: %v\n", numField)
	fmt.Printf("numMethod: %v\n", numMethod)

	for i := 0; i < numField; i++ {
		name := rType.Field(i).Name
		val := rVal.Field(i)
		fmt.Printf("name: %v\tval: %v\n", name, val)
		// name: Num1      val: 30
		// name: Num2      val: 20
	}

	for i := 0; i < numMethod; i++ {
		methodVal := rType.Method(i).Name
		fmt.Printf("methodVal: %v\n", methodVal) // methodVal: GetSub
	}

	var params []reflect.Value = []reflect.Value{reflect.ValueOf("tom")}
	rVal.Method(0).Call(params)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SoaringW

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

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

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

打赏作者

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

抵扣说明:

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

余额充值