反射
常用包而已:strings 、strconv、time、math、reflect、io、os、rutime
正常的开发,很少用到。因为效率低。
开发一些脚手架,自动实现一些底层的判断。
interface{} = any,由于这种动态类型是不确定的,我们可能在底层代码进行判断,从而选择使用什么来处理。
反射机制可以在程序运行的过程中,获取到信息(变量:类型、值。结构体:字段、方法)
我们可以通过反射来修改变量和值、通过反射来调用方法。
reflect 包
所有的方法几乎都在这里面
-
reflect.Type类型typeOf reflect .Value值valueof()
- var age int = 18
- var 变量名 变量类型 = 变量值
- var user User = User{xx,xx}
类型:Type
(在编程中我们使用Type),指代的是某一种类型
种类:Kind
(反射更多时候用Kind来区分),指代的是相似的一些列类型
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())
}
静态类型 & 动态类型
在反射过程中,编译的时候就知道变量类型的就是静态类型、如果在运行时候才知道类型的就是动态类型
静态类型:变量在声明时候给他赋予类型的
var name string // string是静态类型的
var age int // int 是静态类型的
动态类型:在运行的时候可能发生变化,主要考虑赋值问题
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()
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
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)
}
}
修改结构体变量:通过属性名,来实现修改
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方法
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) // 无参方法调用
反射调用函数
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
}