go语言特性--反射
这篇博客简单总结一下go
语言的一个原理--反射。
反射是指在程序运行期间对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。
但是支持反射的语言可以在程序编译期间将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期间获取类型的反射信息,并且有能力修改它们。
1. reflect
包
任何接口值都是由==类型==和==值==组成的。
反射中类型还区分**类型(type)和种类(kind)**两种。
type cat struct{ // ... } // cat 就是类型,而struct是种类 // 种类:struct, 指针, bool, int...
go
的反射功能包---reflect
获取类型和具体的值:
// TypeOf() func f(x interface{}){ // 获取空接口的具体类型 v := reflect.TypeOf(x) fmt.Printf("%v\n", v) // 获取类型和种类 fmt.Printf("%v, %v\n", v.Name(), v.Kind()) } //////////////////////////////////////////////////////////////// // ValueOf() func f(x interface{}){ // 获取空接口的具体值 v := reflect.ValueOf(x) fmt.Printf("%v\n", v) // 获取值得类型和种类 fmt.Printf("%v\n", v.Kind()) }
通过反射设置值:
func f(x interface{}){ // 获取空接口的具体值 v := reflect.ValueOf(x) // 取出v的值并修改 if v.Elem().Kind() == reflect.Int64{ v.Elem().SetInt(100) } } func main(){ var a int64 a = 10 // 要传地址 f(&a) }
2. 结构体反射
type student struct { Name string `json:"name"` Score int `json:"score"` } func main() { stu1 := student{ Name: "小王子", Score: 90, } t := reflect.TypeOf(stu1) fmt.Println(t.Name(), t.Kind()) // student struct // 通过for循环遍历结构体的所有字段信息 for i := 0; i < t.NumField(); i++ { field := t.Field(i) fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json")) } // 通过字段名获取指定结构体字段信息 if scoreField, ok := t.FieldByName("Score"); ok { fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json")) } }
反射可以使得代码更灵活,但是反射也有缺点:
- 代码难以理解。
- 反射的代码性能很低。
3. 反射应用
获取interface{}
空接口类型参数 的具体类型和值。
.ini
配置文件解析器 (所有的配置文件解析都会用到反射)。
json
的序列化和反序列化。
4. reflect
包的其他用处
// 比较无法直接用 == 比较的类型,如切片 reflect.DeepEqual(a,b)
反射是在C语言中没有的特性,也比较难理解,最好的方法就是在项目实战中去体会理解反射的用处。