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语言中没有的特性,也比较难理解,最好的方法就是在项目实战中去体会理解反射的用处。