1. 反射
Go 中的反射是建立在类型系统之上,它与空接口 interface{} 密切相关。
每个 interface{} 类型的变量包含一对值 (type,value),type 表示变量的类型信息,value 表示变量的值信息
reflect.TypeOf() 获取类型信息,返回 Type 类型
reflect.ValueOf() 获取数据信息,返回 Value 类型
struct 反射示例
package main
import (
"fmt"
"reflect"
)
type student struct {
Name string `json:"name"`
Age int `json:"age" id:"1"`
}
func main() {
stu := student{
Name: "lee",
Age: 15,
}
valueOfStu := reflect.ValueOf(stu)
// 获取struct字段数量
fmt.Println("NumFields: ", valueOfStu.NumField())
// 获取字段 Name 的值
fmt.Println("Name value: ", valueOfStu.Field(0).String(), ", ", valueOfStu.FieldByName("Name").String())
// 字段类型
fmt.Println("Name type: ", valueOfStu.Field(0).Type())
typeOfStu := reflect.TypeOf(stu)
for i := 0; i < typeOfStu.NumField(); i++ {
// 获取字段名
name := typeOfStu.Field(i).Name
fmt.Println("Field Name: ", name)
// 获取tag
if fieldName, ok := typeOfStu.FieldByName(name); ok {
tag := fieldName.Tag
fmt.Println("tag-", tag, ", ", "json:", tag.Get("json"), ", id", tag.Get("id"))
}
}
}
反射三大定律:
第一定律:反射是从接口值到反射对象
第二定律:从反射对象可以获取接口值
第三定律:要修改反射对象的值,其值必须可以设置
2. 反射设置变量值
package main
import (
"fmt"
"reflect"
)
type student struct {
Age int
}
func main() {
var stu student
fmt.Println("origin age: ", stu.Age)
valOfStu := reflect.ValueOf(&stu)
canSet := valOfStu.CanSet()
fmt.Println("can set: ", canSet)
valOfStu = valOfStu.Elem()
field := valOfStu.FieldByName("Age")
field.SetInt(23)
fmt.Println("set age: ", field.Int())
//====================
fmt.Println("====================")
var num int32 = 24
v := reflect.ValueOf(&num)
fmt.Println("num:", num, ", elem Kind: ", v.Elem().Kind())
if v.Elem().Kind() == reflect.Int32 {
v.Elem().SetInt(300)
}
fmt.Println("set num: ", num)
}
3. 用反射实现动态调用
package main
import (
"fmt"
"reflect"
)
type Routers struct {
}
func (router *Routers) Hello(args ...interface{}) bool {
fmt.Println(args[0])
return true
}
func (router *Routers) World(args ...interface{}) bool {
fmt.Println(args[0])
return true
}
type FuncCollection map[string]reflect.Value
func main() {
_, _ = CallFunc("Hello", "执行Hello方法")
_, _ = CallFunc("World", "执行World方法")
}
func CallFunc(tableName string, args ...interface{}) (result []reflect.Value, err error) {
var router Routers
FuncMap := make(FuncCollection, 0)
rf := reflect.ValueOf(&router)
rft := rf.Type()
funcNum := rf.NumMethod()
for i := 0; i < funcNum; i++ {
mName := rft.Method(i).Name
// rft.Method(i)
FuncMap[mName] = rf.Method(i)
}
parameter := make([]reflect.Value, len(args))
for k, arg := range args {
parameter[k] = reflect.ValueOf(arg)
}
result = FuncMap[tableName].Call(parameter)
return
}
4. 反射优缺点
优点
可以根据条件灵活的调用函数。最大一个优点就是灵活。
比如函数参数的数据类型不确定,这时可以根据反射来判断数据类型,在调用适当的函数。
还有比如根据某些条件来调用哪个函数。
需要根据动态需要来调用函数,可以用反射。
使用反射的 2 个典型场景:1、操作数据库的 ORM 框架 ,2、依赖注入
缺点
用反射编写的代码比较难以阅读和理解
反射是在运行时才执行,所以编译期间比较难以发现错误
反射对性能的影响,比一般正常运行代码慢一到两个数量级。
这里性能其实是你的业务量到了一定时候才要注意。量不大情况,够用。