反射 (relect包)
大约 3 分钟
反射 (relect包)
核心:relect包
概念
Go反射可以做什么?
- 运行时动态获取:变量的各种信息
- 普通变量的 类型、类别等
- 结构体的 字段、方法
- 运行时动态修改和调用
- 修改变量的值
- 调用关联的方法
相关函数
// `reflect包.TypeOf(变量名)`,获取变量的类型,返回reflect.Type类型
func TypeOf(i interface{}) Type
// `reflectb包.ValueOf(变量名)`,获取变量的值,返回reflect.Value类型。通过该类型可以获取该变量很多信息
func ValueOf(i interface{}) Value
两个主要方法和类型
TypeOf
见下,代码连在一起了
ValueOf
package main
import(
"fmt"
"reflect"
)
func testReflect(i interface{}) { // 100 (`int` -> `interface{}`, 空接口)
// TypeOf
reType := reflect.TypeOf(i)
fmt.Println("reType:", reType) // int
// ValueOf
reValue2 := reflect.ValueOf(i) // 100 (`interface{}` -> `reflect.Value`)
fmt.Println("reValue:", reValue2)
}
type Student struct{
Name string
Age int
}
func main() {
// 基本类型
var num int = 100
testReflect(num)
// 结构体类型
student := Student {
Name: "Lili",
Age: 18,
}
testReflect(student)
}
reflect.Type
获取变量的类别
获取变量类别有两种方式:
k1 := reType.Kind()
fmt.Println(k1) // struct
k2 := reValue.Kind()
fmt.Println(k2) // struct
注意区分:类别 与 类型
Kind方法只能获取到是struct (类别),并无法获取到类名 (类型)
reflect.Value
类型互转
基本类型
基本、interface{}、reflect.Value 三者互转
num1 := 100
var inter1 interface{} = num1 // 100 (`int` -> `interface{}`)
num2, _ := inter2.(int) // 100 (`interface{}` -> `int`) 类型断言
reValue2 := reflect.ValueOf(num1) // 100 (`int` -> `reflect.Value`)
reValue := reflect.ValueOf(inter1) // 100 (`interface{}` -> `reflect.Value`)
num3 := reValue.Int() // 100 (`reflect.Value` -> `int`)
inter2 := reValue.Interface() // 100 (`reflect.Value` -> `interface{}`)
结构体
结构体、interface{}、reflect.Value 三者互转
student := Student {
Name: "Lili",
Age: 18,
}
var inter1 interface{} = stu1 // Lili 18 (`XStruct` -> `interface{}`)
stu2, flag := inter2.(Student) // Lili 18 (`interface{}` -> `XStruct`) 类型断言,需要判断flag
reValue2 := reflect.ValueOf(stu1) // Lili 18 (`XStruct` -> `reflect.Value`)
reValue := reflect.ValueOf(inter1) // Lili 18 (`interface{}` -> `reflect.Value`)
stu3, _ := rV.Interface().(Student) // Lili 18 (`reflect.Value` -> `XStruct`) 这里无法直接转换,需要先转空接口再用类型断言
inter2 := reValue.Interface() // Lili 18 (`reflect.Value` -> `interface{}`)
修改变量的值
基本类型 (.Elem、.setInt)
需要用到一个接口
// 原型
// @param v Elem方法会返回v保管值的Value封装,或v持有指针指向值的Value封装。如果v的Kind不是Interface或Ptr会Panic
// @return 如果v持有值为nil,会返回Value零值
func (v Value) Elem() Value
首先这里修改成传递地址
// 变量准备
var i1 = 100
var inter1 interface{} = &i1
// 修改值
reValue := reflect.ValueOf(inter1) // 对应 `*int`
reValue2 := reValue.Elem() // 对应 `int引用`
reValue2.SetInt(40) // 修改值
fmt.println(i1) // 40
结构体 (多了个Field()方法)
// 变量准备
type Student struct{
Name string
Age int
}
s := Student{
Name: "Lili"
Age: 18
}
var inter1 interface{} = &s
// 修改值
reValue := reflect.ValueOf(inter1) // 对应 `*Student`
reValue2 := reValue.Elem() // 对应 `Student引用`
//n := reValue2.NumField() // 字段数。2
reValue2.Field(0).SetString("LiSi") // 修改值
操作结构体的字段和方法
package main
import(
"fmt"
"reflect"
)
// 类
type Student struct{
Name string
Age int
}
func (s Student) CPrint() {
fmt.Println("调用了Print方法")
fmt.Println("学生名:", s.Name)
}
func (s Student) AGetSum(n1, n2 int) int {
return n1+n2
}
func (s Student) BSet(name string, age int) int {
s.Name = name
s.Age = age
}
// 操作结构体进行反射
func TestStudentStruct(a interface{}){
reV := reflect.ValueOf(a) // `interface{}` -> `reflect.Value`
// 结构体字段
n1 := reV.NumField() // 获取结构体字段数量。打印:2
for i:=0; i<n1; i++{ // 获取结构体字段内容
fmt.Printf("第%d个字段的值为:%v\n", i, val.Field(i))
}
// 结构体方法
n2 := reV.NumMethod() // 获取结构体方法数量。打印:3
vaV.Method(2).Call(nil) // 调用结构体方法。 (需要注意,这玩意居然是按字母而非定义顺序排序的。这里调用的是CPrint方法)
}
// 主函数
func main() {
s := Student{
Name: "Lili"
Age: 18
}
TestStudentStruct(s)
}