反射go语言

75 阅读5分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

反射的引用和应用

在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。
也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),
并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
每种语言的反射模型都不同,并且有些语言根本不支持反射。Golang语言实现了反射,
反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。

反射基本介绍

反射重要函数和变量

变量,interface{},reflect.Value是可以相互转换的.

反射的快速入门

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

package main

import (
	"fmt"
	"reflect"
)

func reflectTest01(b interface{}) {
	//通过反射获取传入的值的type,kind,值
	//1.先获取 reflect.Type
	rTyp := reflect.TypeOf(b)
	fmt.Println("rTyp= ", rTyp) //rTyp= int

	//2.获取到reflect.value
	rval := reflect.ValueOf(b)
	fmt.Println("值 ", rval)  //值  100
	fmt.Printf("%T\n", 100)  //int
	fmt.Printf("%T\n", rval) //reflect.Value: reflect.Value类型 并不是真正的100整数

	n2 := 2 + rval.Int()
	fmt.Println("n2= ", n2)

	//将rval转换成interface()
	iV := rval.Interface()
	num2 := iV.(int)
	fmt.Printf("%T %d\n", num2, num2)
}
func main() {
	//1.定义一个int
	var num int = 100
	reflectTest01(num)
}

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

// 断言

package main
import (
	"fmt"
)
//编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items...interface{}){
	for index,x:=range items {
		switch x.(type){
		case bool:
			fmt.Printf("第%v个参数是 bool 类型,值是%v\n",index,x)
		case float32:
			fmt.Printf("第%v个参数是 float32 类型,值是 %v\n",index,x)
		case float64:
			fmt.Printf("第%v个参数是 float64 类型,值是 %v\n",index,x);
		case int,int32,int64:
			fmt.Printf("第%v个参数是 整数 类型,值是 %v\n",index,x)
		case string:
			fmt.Printf("第%v个参数是 string 类型,值是 %v\n",index,x)
		default:
			fmt.Printf("第%v个参数类型不确定,值是 %v\n",index,x);
		}	
	}
}
func main(){
	var n1 float32=1.14
	var n2 float64=1.33
	var n3 int32=11
	var name string="tom"
	addres:="长沙"
	n4:=300
	TypeJudge(n1,n2,n3,name,addres,n4)
}
/*
第0个参数是 float32 类型,值是 1.14
第1个参数是 float64 类型,值是 1.33
第2个参数是 整数 类型,值是 11
第3个参数是 string 类型,值是 tom
第4个参数是 string 类型,值是 长沙
第5个参数是 整数 类型,值是 300
*/
package main

import (
	"fmt"
	"reflect"
)

func reflectTest02(b interface{}) {
	//1.先获取到 reflect.Type
	rTyp := reflect.TypeOf(b)
	fmt.Println("rType= ", rTyp)

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

	//下面我们将rVal 转成 interface{}
	iV := rVal.Interface()
	fmt.Printf("iv =%v iv type= %T \n", iV, iV)
	//注意反射运行是确定类型直接	iV.Name 会报错
	
	//使用断言取类型
	stu, ok := iV.(Student)
	if ok {
		fmt.Printf("stu.Name=%v\n", stu.Name)
	}
}

type Student struct {
	Name string
	Age  int
}

func main() {
	stu := Student{Name: "my_name", Age: 11}
	reflectTest02(stu)

}

//通过反射进行改值
package main

import (
	"fmt"
	"reflect"
)

//通过反射,修改
//num int的值
//修改 student 的值
func reflect01(b interface{}) {
	//获取到 reflect.Value
	rVal := reflect.ValueOf(b)
	//看 rVal的kind
	fmt.Printf("rVal kind= %v\n", rVal.Kind())//rVal kind= ptr
	rVal.Elem().SetInt(20)
}
func main() {
	var num int = 10
	reflect01(&num)
	fmt.Printf("%d\n", num)//20
}

反射的实践

方法

func (Value) Method
func (v Value) Method(i int) Value
返回v持有值类型的第i个方法的已绑定(到v的持有值的)状态的函数形式的Value封装。
返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。
如果i出界,或者v的持有值是接口类型的零值(nil),会panic。

func (v Value) Call(in []Value) []Value
Call方法使用输入的参数in调用v持有的函数。
例如,如果len(in) == 3,v.Call(in)代表调用v(in[0], in[1], in[2])(其中Value值表示其持有值)。
如果v的Kind不是Func会panic。它返回函数所有输出结果的Value封装的切片。
和go代码一样,每一个输入实参的持有值都必须可以直接赋值给函数对应输入参数的类型。
如果v持有值是可变参数函数,Call方法会自行创建一个代表可变参数的切片,将对应可变参数的值都拷贝到里面。

1)使用反射

来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值

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)	
}