[TOC] # 变量的内在机制 1. 类型信息,这部分是元信息,是预先定义好的 2. 值类型,这部分是程序运行过程中,动态改变的 反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。 多插一句,Golang的gRPC也是通过反射实现的。 # 反射与空接口 空接口可以存储任何类型的变量 那么给你一个空接口,怎么判断里面存储的是什么东西? 在运行时动态的获取一个变量的类型和值信息就叫反射 内置包: reflect 获取类型信息: reflect.TypeOf 获取值信息: reflect.ValueOf # 反射种类(kind)定义 Go程序中的类型(Type)指的是系统原生数据类型,如int、string、bool、float32等类型,以及使用type关键字定义的类型,这些类型的名称就是其类型本身的名称。例如使用typeAstruct{}定义结构体时,A就是struct{}的类型。种类(Kind)指的是对象归属的品种,在reflect包中有如下定义 ~~~ type Kind uint const ( Invalid Kind = iota // 非法类型 Bool // 布尔型 Int // 有符号整型 Int8 // 有符号8位整型 Int16 // 有符号16位整型 Int32 // 有符号32位整型 Int64 // 有符号64位整型 Uint // 无符号整型 Uint8 // 无符号8位整型 Uint16 // 无符号16位整型 Uint32 // 无符号32位整型 Uint64 // 无符号64位整型 Uintptr // 指针 Float32 // 单精度浮点数 Float64 // 双精度浮点数 Complex64 // 64位复数类型 Complex128 // 128位复数类型 Array // 数组 Chan // 通道 Func // 函数 Interface // 接口 Map // 映射 Ptr // 指针 Slice // 切片 String // 字符串 Struct // 结构体 UnsafePointer // 底层指针 ) ~~~ Map、Slice、Chan属于引用类型,使用起来类似于指针,但是在种类常量定义中仍然属于独立的种类,不属于Ptr。type A struct{}定义的结构体属于Struct种类,\*A属于Ptr # TypeOf和ValueOf ~~~ func reflect_example(a interface{}) { t := reflect.TypeOf(a) fmt.Printf("type of a is %v\n", t) v := reflect.ValueOf(a) fmt.Printf("value of a is %v\n", v) k := t.Kind() fmt.Println(k) } func main() { var x int64 = 3 reflect_example(x) } ~~~ # 运行时修改值 ~~~ func reflect_example(a interface{}) { v := reflect.ValueOf(a) k := v.Kind() switch k { case reflect.Int64: fmt.Printf("a is int64, store value is: %d\n", v.Int()) case reflect.Float64: fmt.Printf("a is Float64, store value is: %f\n", v.Float()) case reflect.Ptr: //指针类型 .Elem()相当于 指针取值 v.Elem().SetFloat(22.5) fmt.Println("指针") default: fmt.Println("default") } } func main() { var x float64 = 3.4 reflect_example(&x) fmt.Println(x) } ~~~ ~~~ var x float64 = 3.4 //这边要传地址,不然反射的是副本,下面修改副本的值会报错 v1 := reflect.ValueOf(&x) //这边已经是指针,要用Elem,通过Elem()获取指针指向的变量,从而完成赋值操作 v1.Elem().SetFloat(4.3) fmt.Println(v1.Elem().Float()) ~~~ # 结构体属性 ~~~ type Student struct { Name string Sex int Age int //abc string } func main() { var s Student v := reflect.ValueOf(s) t := v.Type() kind := t.Kind() fmt.Println(kind) //struct //查看字段数量,包含私有字段 fmt.Println(v.NumField()) //注意,私有获取不到,会报错的 for i := 0; i < v.NumField(); i++ { field := v.Field(i) fmt.Printf("名字: %s 类型: %v 值: %v\n", t.Field(i).Name, field.Type(), field.Interface()) } //修改结构体内部的值 v1 := reflect.ValueOf(&s) //用索引方式 v1.Elem().Field(0).SetString("abc") //指定名称方式 v1.Elem().FieldByName("Sex").SetInt(2) v1.Elem().FieldByName("Age").SetInt(12) fmt.Println(s) } ~~~ 输出 ~~~ struct 3 名字: Name 类型: string 值: 名字: Sex 类型: int 值: 0 名字: Age 类型: int 值: 0 {abc 2 12} ~~~ # 结构体方法 ~~~ func (s *Student) Test() { fmt.Println("this is test") } func main() { s := Student{23, "skidoo"} v := reflect.ValueOf(&s) t := v.Type() v.Elem().Field(0).SetInt(100) fmt.Println("method num: ", v.NumMethod()) for i := 0; i < v.NumMethod(); i++ { f := t.Method(i) fmt.Printf("%d method, name: %v, type: %v\n", i, f.Name, f.Type) } } ~~~ 输出 ~~~ method num: 1 0 method, name: Test, type: func(*main.Student) ~~~ # 调用结构体方法 ~~~ type Student struct { A int B string } func (s *Student) Test() { fmt.Println("this is test") } func (s *Student) SetA (a int) { s.A = a } func main() { s := Student{23, "skidoo"} //要引用传递,不然修改的是副本会报错 v := reflect.ValueOf(&s) m := v.MethodByName("Test") var args1 []reflect.Value m.Call(args1) setA := v.MethodByName("SetA") var args2 []reflect.Value //参数 args2 = append(args2, reflect.ValueOf(100)) setA.Call(args2) fmt.Printf("s: %#v\n", s) } ~~~ 输出 ~~~ this is test s: main.Student{A:100, B:"skidoo"} ~~~ # 获取结构体中的tag信息 ~~~ type Student struct { F string `species:"gopher" color:"blue" json:"f"` } func main() { s := Student{} //要引用传递,不然修改的是副本会报错 v := reflect.TypeOf(s) field := v.Field(0) fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"), field.Tag.Get("json")) ~~~ 输出 ~~~ blue gopher f ~~~