# 1. 反射基础概念 - **定义**: 在程序运行时动态获取和操作变量信息的机制 - **主要用途**: - 获取变量类型和值 - 访问和修改结构体字段 - 动态调用方法 - 实现框架底层逻辑 # 2. 使用场景 - **适用场景**: - 开发框架和脚手架 - 实现通用性工具 - 处理未知类型的数据 - **使用建议**: - 一般开发中谨慎使用 - 反射操作性能开销较大 - 代码复杂度会增加 # 3. 核心概念 ## Type 和 Kind 的区别 ```go // Type: 具体类型 var user User = User{name: "Tom", age: 20} // Kind: 底层类型分类 var num int = 18 ``` - **Type(类型)**: - 具体的数据类型 - 例如: `User`, `Student`, `int64` - **Kind(种类)**: - 类型的底层分类 - 例如: `struct`, `int`, `string` # 4. reflect 包核心功能 ## 主要 API ```go // 获取类型信息 typeInfo := reflect.TypeOf(变量) // 获取值信息 valueInfo := reflect.ValueOf(变量) ``` ## 常用操作 ```go // 类型判断 if typeInfo.Kind() == reflect.Struct { // 处理结构体 } // 获取/修改值 if valueInfo.Kind() == reflect.Int { value := valueInfo.Int() valueInfo.Set(reflect.ValueOf(20)) } ``` # 5. 实际应用示例 ## 结构体反射 ```go type User struct { Name string Age int } func inspectStruct(v interface{}) { t := reflect.TypeOf(v) for i := 0; i < t.NumField(); i++ { field := t.Field(i) fmt.Printf("字段名: %s, 类型: %s\n", field.Name, field.Type) } } ``` ## 方法反射 ```go func (u User) SayHello(name string) { fmt.Printf("Hello, %s\n", name) } // 通过反射调用方法 func callMethod(v interface{}) { val := reflect.ValueOf(v) method := val.MethodByName("SayHello") args := []reflect.Value{reflect.ValueOf("Tom")} method.Call(args) } ``` # 6. 性能考虑 - 反射操作比直接操作慢 10-100 倍 - 建议在以下场景使用: - 框架开发 - 配置解析 - 序列化/反序列化 - 避免在性能敏感代码中使用 # 7. 最佳实践 1. 优先使用类型断言代替反射 2. 缓存反射结果避免重复操作 3. 谨慎使用反射修改值 4. 做好错误处理 5. 编写清晰的文档说明 ```go package main import ( "fmt" "reflect" ) // 反射 /* Type : reflect.TypeOf(a) , 获取变量的类型 Value :reflect.ValueOf(a) , 获取变量的值 */ func main() { // 正常编程定义变量 var a int = 3 fmt.Println("type", reflect.TypeOf(a)) fmt.Println("value", reflect.ValueOf(a)) // 根据反射的值,来获取对象对应的类型和数值 // 如果我们不知道这个对象的信息,我们可以通过这个对象拿到代码中的一切。 // Value v := reflect.ValueOf(a) // string int User // Kind : 获取这个值的种类, 在反射中,所有数据类型判断都是使用种类。 if v.Kind() == reflect.Float64 { fmt.Println(v.Float()) } if v.Kind() == reflect.Int { fmt.Println(v.Int()) } fmt.Println(v.Kind() == reflect.Float64) //fmt.Println(v.Type()) } ``` > 静态类型 & 动态类型 在反射过程中,编译的时候就知道变量类型的就是静态类型、如果在运行时候才知道类型的就是动态类型 静态类型:变量在声明时候给他赋予类型的 ```go var name string // string是静态类型的 var age int // int 是静态类型的 ``` 动态类型:在运行的时候可能发生变化,主要考虑赋值问题 ```go var A interface{} // interface{} 静态类型 A = 10 // interface{} 静态类型 动态类型 int A = "xuexiangban" // interface{} 静态类型 动态类型 string ``` **为什么要用反射:** 1、我们需要编写一个函数,但是不知道函数传递给我的参数时什么?没约定好,传入的类型太多,这些类型不能统一表示,反射。 2、我们在某些使用,需要根据条件来判断具体使用哪个函数处理问题,根据用户的输入来决定,这时候就需要对函数的参数进行反射,在运行期间来动态处理。 **为什么不建议使用反射:** 1、和反射相关的代码,不方便阅读,开发中,代码可读性(指标)很重要 2、Go语言是静态类型的语言,编译器可以找出开发时候的错误,如果代码中有大量反射代码,随时可能存在安全问题,panic,项目就终止。 3、反射的性能很低,相对于正常的开发,至少慢2-3个数量级。项目关键位置低耗时,一定是不能使用反射的。更多时候使用约定。 # 反射获取变量信息 **实现拿到一个对象,还原它的本身结构信息** reflect.TypeOf(v) : 获取变量的类型 Type - Type.kind , 找到种类 - Type.NumField(),找到里面字段的数量 - Type.Filed(i) - Type.NumMethod(),找到里面方法的数量 - Type.Method(i) reflect.ValueOf(v) : 获取变量的值 Value - Value.Field(i).Interface() ```go package main import ( "fmt" "reflect" ) type User struct { Name string Age int Sex string } func (user User) Say(msg string) { fmt.Println("User 说:", msg) } // toString : 打印结构体信息 func (user User) PrintInfo() { fmt.Printf("姓名:%s,年龄:%d,性别:%s\n", user.Name, user.Age, user.Sex) } func main() { user := User{"kuangshen", 18, "男"} reflectGetInfo(user) } // 通过反射,获取变量的信息 func reflectGetInfo(v interface{}) { // 1、获取参数的类型Type , 可能是用户自己定义的,但是Kind一定是内部类型struct getType := reflect.TypeOf(v) fmt.Println(getType.Name()) // 类型信息 User fmt.Println(getType.Kind()) // 找到上级的种类Kind struct // 2、获取值 getValue := reflect.ValueOf(v) fmt.Println("获取到value", getValue) // 获取字段,通过Type扒出字段 // Type.NumField() 获取这个类型中有几个字段 3 // field(index) 得到字段的值 for i := 0; i < getType.NumField(); i++ { field := getType.Field(i) // 类型 value := getValue.Field(i).Interface() // value // 打印 fmt.Printf("字段名:%s,字段类型:%s,字段值:%v\n", field.Name, field.Type, value) } // 获取这个结构体的方法 , 获取方法的数量 for i := 0; i < getType.NumMethod(); i++ { method := getType.Method(i) fmt.Printf("方法的名字:%s,方法类型:%s", method.Name, method.Type) } } /* type User struct{ Name string Age int Sex string } func (main.User) PrintInfo(){} func (main.User) Say(string) */ ``` # 反射修改变量的值 vaule.CanSet vaule.SetXXX ```go package main import ( "fmt" "reflect" ) // 反射设置变量的值 func main() { var num float64 = 3.14 update(num) fmt.Println(num) } func update(v any) { // 通过反射修改值,需要操作对象的指针,拿到地址,然后拿到指针对象 pointer := reflect.ValueOf(&v) newValue := pointer.Elem() fmt.Println("类型:", newValue.Type()) fmt.Println("判断该类型是否可以修改:", newValue.CanSet()) if newValue.Kind() == reflect.Float64 { // 通过反射对象给变量赋值 newValue.SetFloat(2.21) } if newValue.Kind() == reflect.Int { // 通过反射对象给变量赋值 newValue.SetFloat(2) } } ``` 修改结构体变量:通过属性名,来实现修改 ```go func main() { user := User{"kuangshen", 18, "男"} value := reflect.ValueOf(&user) if value.Kind() == reflect.Ptr { // 获取指针对象 newValue := value.Elem() if newValue.CanSet() { // 如果是结构体:1、需要找到对象的结构体字段名 newValue.FieldByName("Name").SetString("狂神") newValue.FieldByName("Age").SetInt(26) } } fmt.Println(user) //reflectGetInfo(user) } ``` # 反射调用方法 > value.MethodByName("PrintInfo").Call(nil) > > // 参数构建 > > args := make([]reflect.Value, 1) > args[0] = reflect.ValueOf("这反射来调用的") 通过反射调用user方法 ```go user := User{"kuangshen", 18, "男"} // 通过方法名,找到这个方法, 然后调用 Call() 方法来执行 // 反射调用方法 value := reflect.ValueOf(user) fmt.Printf("kind:%s,type:%s\n", value.Kind(), value.Type()) value.MethodByName("PrintInfo").Call(nil) // 无参方法调用 // 有参方法调用 args := make([]reflect.Value, 1) args[0] = reflect.ValueOf("这反射来调用的") value.MethodByName("Say").Call(args) // 无参方法调用 ``` # 反射调用函数 ```go package main import ( "fmt" "reflect" ) // 反射调用函数 func func main() { // 通过函数名来进行反射 // Kind func value1 := reflect.ValueOf(fun1) fmt.Println(value1.Kind(), value1.Type()) value1.Call(nil) value2 := reflect.ValueOf(fun2) fmt.Println(value2.Kind(), value2.Type()) args1 := make([]reflect.Value, 2) args1[0] = reflect.ValueOf(1) args1[1] = reflect.ValueOf("hahahhaha") value2.Call(args1) vuale3 := reflect.ValueOf(fun3) fmt.Println(vuale3.Kind(), vuale3.Type()) args2 := make([]reflect.Value, 2) args2[0] = reflect.ValueOf(1) args2[1] = reflect.ValueOf("hahahhaha") resultValue := vuale3.Call(args2) fmt.Println("返回值:", resultValue) } func fun1() { fmt.Println("fun1:无参") } func fun2(i int, s string) { fmt.Println("fun2:有参 i=", i, " s=", s) } func fun3(i int, s string) string { fmt.Println("fun3:有参有返回值 i=", i, " s=", s) return s } ```