Golang反射

Golang 反射

程序运行期间对程序本身进行访问和修改的能力

reflect.TypeOf() 获取对象的Type

reflect.ValueOf() 获取对象的Value

//TypeOf 这个函数返回任何表达式的类型
	var a int = 64
	s := reflect.TypeOf(a)
	fmt.Println(s.Name()) //类型名称
	fmt.Println(s.Kind()) //类型种类,底层的类型
	fmt.Println(s)        //int
	//ValueOf 这个函数返回任何表达式的值
	b := reflect.ValueOf(a)
	fmt.Println(b.Kind())
	fmt.Println(b) //64

TypeOf中还有两个方法 , Name获取类型的名称,kind 获取地城的类型种类

Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()都是返回空。

kind的常用类型如下

Bool 布尔型
Int 有符号整型
int8 16 64 有符合8 16 64位整型
Uint 无符号整型
Uint 8 16 64 无符号8 16 64位整型
UintPtr 指针
Float 32 64 单精度和双精度浮点数
Complex64 128 64位和128位复数类型
Array 数组
Chan 通道
Func 函数
interface 接口
Map 映射
Ptr 指针
Slice 切片
String 字符串
Struct 结构体
UnsafePointer 底层指针

reflect.ValueOf()

提供的获取原始值的方法如下

方法 说明
interface{} Interface() 将值以interface{}类型返回,可以通过类型断言转换为指定类型
Int() int64 将值以int类型返回
Uint() uint64 将值以uint类型返回
Float() float64 将值以双精度float64类型返回
Book() bool 将值以bool类型返回
Bytes() []bytes 将值以字节数组[]bytes类型返回
String() string 将值以字符串类型返回

通过反射设置变量的值

SetBool(x bool)

SetInt(x int64)

SetString(x string) 等

想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地 址才能修改变量值。而反射中使用专有的 Elem()方法来获取指针对应的值。

//通过反射修改变量的值
	var a int =32
	v := reflect.ValueOf(&a)
	if v.Kind() == reflect.Ptr && !v.Elem().CanSet() {
		return
	}
	v.Elem().SetInt(86)
	fmt.Println(a) //86

结构体反射

任意值通过 reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体,可以通过反射值 对象(reflect.Type)的 NumField()和 Field()方法获得结构体成员的详细信息

方法 说明
Field(i int) StructField 根据索引,返回索引对应的结构体字段的信 息。
NumField() int 返回结构体成员字段数量。
FieldByName(name string) (StructField, bool) 根据给定字符串返回字符串对应的结构体字 段的信息。
FieldByIndex(index []int) StructField 多层成员访问时,根据 []int 提供的每个结构 体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool) 根据传入的匹配函数匹配需要的字段。
NumMethod() int 返回该类型的方法集中方法的数目
Method(int) Method 返回该类型方法集中的第 i 个方法
MethodByName(string)(Method, bool) 根据方法名返回该类型方法集中的方法
//student结构体
type Student struct {
	Name  string `json:"name1" form:"username"`
	Age   int    `json:"age"`
	Score int    `json:"score"`
}

func (s Student) GetInfo() string {
	var str = fmt.Sprintf("姓名:%v 年龄:%v 成绩:%v", s.Name, s.Age, s.Score)
	return str
}

func (s *Student) SetInfo(name string, age int, score int) {
	s.Name = name
	s.Age = age
	s.Score = score
}

func (s Student) Print() {
	fmt.Println("这是一个打印方法...")
}

//打印字段
func PrintStructField(s interface{}) {

	//判断参数是不是结构体类型
	t := reflect.TypeOf(s)
	v := reflect.ValueOf(s)
	if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
		fmt.Println("传入的参数不是一个结构体")
		return
	}

	//1、通过类型变量里面的Field可以获取结构体的字段
	field0 := t.Field(0)
	fmt.Printf("%#v \n", field0) //reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x4adf20), Tag:"json:\"name\"", Offset:0x0, Index:[]int{0}, Anonymous:false}
	fmt.Println("字段名称:", field0.Name)
	fmt.Println("字段类型:", field0.Type)
	fmt.Println("字段Tag:", field0.Tag.Get("json"))
	fmt.Println("字段Tag:", field0.Tag.Get("form"))
	//2、通过类型变量里面的FieldByName可以获取结构体的字段
	fmt.Println("----------------------")
	field1, ok := t.FieldByName("Age")
	if ok {
		fmt.Println("字段名称:", field1.Name)
		fmt.Println("字段类型:", field1.Type)
		fmt.Println("字段Tag:", field1.Tag.Get("json"))
	}

	//3、通过类型变量里面的NumField获取到该结构体有几个字段

	var fieldCount = t.NumField()
	fmt.Println("结构体有", fieldCount, "个属性")

	//4、通过值变量获取结构体属性对应的值

	fmt.Println(v.FieldByName("Name"))
	fmt.Println(v.FieldByName("Age"))
	fmt.Println("----------------------")
	for i := 0; i < fieldCount; i++ {
		fmt.Printf("属性名称:%v 属性值:%v 属性类型:%v 属性Tag:%v\n", t.Field(i).Name, v.Field(i), t.Field(i).Type, t.Field(i).Tag.Get("json"))
	}

}

//打印执行方法
func PrintStructFn(s interface{}) {

	t := reflect.TypeOf(s)
	v := reflect.ValueOf(s)
	if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
		fmt.Println("传入的参数不是一个结构体")
		return
	}
	//1、通过类型变量里面的Method可以获取结构体的方法
	method0 := t.Method(0)    //和结构体方法的顺序没有关系,和结构体方法的ASCII有关系
	fmt.Println(method0.Name) //GetInfo
	fmt.Println(method0.Type) //func(main.Student) string

	fmt.Println("--------------------------")
	//2、通过类型变量获取这个结构体有多少个方法

	method1, ok := t.MethodByName("Print")
	if ok {
		fmt.Println(method1.Name) //Print
		fmt.Println(method1.Type) //func(main.Student)
	}
	fmt.Println("--------------------------")
	//3、通过《值变量》执行方法 (注意需要使用值变量,并且要注意参数) v.Method(0).Call(nil) 或者v.MethodByName("Print").Call(nil)
	// v.Method(1).Call(nil)
	v.MethodByName("Print").Call(nil)

	info1 := v.MethodByName("GetInfo").Call(nil)
	fmt.Println(info1)
	//4、执行方法传入参数 (注意需要使用《值变量》,并且要注意参数,接收的参数是[]reflect.Value的切片)

	var params []reflect.Value
	params = append(params, reflect.ValueOf("李四"))
	params = append(params, reflect.ValueOf(23))
	params = append(params, reflect.ValueOf(99))
	v.MethodByName("SetInfo").Call(params) //执行方法传入参数

	info2 := v.MethodByName("GetInfo").Call(nil)
	fmt.Println(info2)

	// 5、获取方法数量

	fmt.Println("方法数量:", t.NumMethod())

}

func main() {
	stu1 := Student{
		Name:  "小明",
		Age:   15,
		Score: 98,
	}
	// PrintStructField(stu1)
	PrintStructFn(&stu1)

	fmt.Printf("%#v\n", stu1)
}

Golang反射
http://www.jcwit.com/article/501/
作者
Carlos
发布于
2024年3月19日
许可协议