每日一Go-32、Go深入-反射原理、性能成本与最佳实践
文末有源码下载链接! 如果说“Go语言类型系统”像人生的角色系统,那么反射就是你拿着一面可以“照见本质”的镜子,透过镜子,你能看到一个对象的真实身份、字段、方法,以及它隐藏的全部能力。 但镜子是神奇的,也是危险的: 一、为什么需要反射? 在静态语言中,我们通常希望类型在编译器已知,这能让: 但某些场景中,你必须在运行期处理一个“我不知道是什么类型”的对象,例如: 这些地方,你必须动态获取字段名、方法名、类型信息... 此时,反射就是你唯一的工具。 二、反射的核心三件套Type、Value、Kind 1. Type:类型信息 2. Value:值信息 Value描述一个对象的真实值: 3. Kind:底层分类 Kind不是类型,而是类型的底层归类: 三、反射的基本原理:接口+元数据 反射=从接口的运行时类型信息中,恢复出真实类型与值。 接口存两部分: 反射只是把这俩拿出来,再给我们操作的能力。 四、反射的三大能力 Go的反射非常克制,也非常强大,分为三个层级: 1. 检查一个值 你可以: 你可以通过反射读取struct标签,这也是JSON/ORM能自动映射的原因 2. 修改一个值 你要用Elem()并传指针。 3. 调用方法(Call) 五、反射的危险与性能成本 反射是高成本、低安全性的,应谨慎使用。为什么?因为: 1. 反射是运行时的行为,慢 原因: 2. 缺乏编译期检查,容易犯错 3. 破坏类型系统,变成“动态语言” 很多新手滥用反射,让代码变得: 六、最佳实践 1. 能不用反射就不用 优先顺序:普通代码->接口->泛型->最后才是反射 2. 反射只用于框架层,而不是业务层 哪些地方应该用反射? 3. 反射的结果要缓存 不要在循环中反复反射类型。 4. 用反射式非常严格的:必须先判断Kind 七、实战,利用反射实现一个小型ORM标签解析器 写一个函数,返回所有字段对应的数据库列: 调用 *源码地址* 1、公众号“Codee君”回复“每日一Go”获取源码 2、https://pan.baidu.com/s/1B6pgLWfSgMngVeFfSTcPdg?pwd=jc1s 反射是一面“照见真相的镜子”,你可以看到一个对象的真实身份、能力和内部结构;但这面镜子沉重、昂贵、易碎,只有在真正必要的时候,你才应当举起它。 如果您喜欢这篇文章,请点赞、推荐+分享给更多朋友,万分感谢!
reflect.Type
reflect.Value
reflect.Kindt := reflect.TypeOf(x)
v := reflect.ValueOf(x)type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func (u *User) SetName(name string) {
u.Name = name
fmt.Println("Name updated to", u.Name)
}func TestT(t *testing.T) {
u := User{
ID: 1,
Name: "张三",
}
tp := reflect.TypeOf(u)
for i := 0; i < tp.NumField(); i++ {
f := tp.Field(i)
fmt.Println(f.Name, f.Tag.Get("json"))
}
}//修改name
v := reflect.ValueOf(&u).Elem()
v.FieldByName("Name").SetString("李四")
fmt.Println(u)
// 输出 {1 李四}//调用Call
m := reflect.ValueOf(&u).MethodByName("SetName")
m.Call([]reflect.Value{reflect.ValueOf("王五")})
fmt.Println(u)
// 输出 {1 王五}FieldByName
"age"
// 字段写错也不会报错var
.Map
[reflect.Type][]if v.Kind() != reflect.Struct {
return errors.New("must be struct")
}type User struct {
ID int `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Age int `json:"age" db:"age"`
}func Parse(v interface{}) map[string]interface{} {
t := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
val = val.Elem()
}
res := make(map[string]interface{})
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
tag := f.Tag.Get("db")
if tag == "" {
continue
}
res[tag] = val.Field(i).Interface()
}
return res
}u2 := User{
ID: 1,
Name: "张三",
Age: 10,
}
fmt.Println(Parse(u2))
// 输出 map[age:10 id:1 name:张三]