Go编程之反射reflect
Admin
2022-05-23 17:45:16
GoLang
反射是程序执行时检查其所拥有的结构,尤其是类型的一种能力,这是元编程的一种形式,同时也是造成混淆的来源。
每一种语言的反射模型都不同(很多语言根本不支持反射,Python通过hasattr方法实现)Go语言中的反射通过reflect包实现,实现了运行时反射,允许程序操作任意类型的对象。
reflect包中的两个数据类Type和Value:
Type:表示一个Go类型(不是所有Go类型的Type值都能使用所有方法,可参见每个方法的文档获取使用限制,在调用有分类限定的方法时,应先使用Kind方法获知类型的分类,调用该分类不支持的方法会导致运行时的panic)。
package main import ( "reflect" "fmt" ) type S interface { Get() Post() } type A struct { Name string `json:"name"` Value string `json:"value"` } func (a *A) Get() { fmt.Println("Get is Name: ", a.Name) } func (a *A) Post() { fmt.Println("Post is Name: ", a.Name) } func main() { p := A{Name: "ywfuns.com", Value: "www"} n := new(S) k := reflect.TypeOf(p) fmt.Println(k.Name()) // 对象名称,不含包名;即A fmt.Println(k.String()) // 对象名称,含包名;即main.A fmt.Println(k.PkgPath()) // 包名 fmt.Println(k.Kind()) // 对象类型,即struct fmt.Println(k.Size()) // 存储的字节大小 fmt.Println(k.NumField()) // 获取字段数量 fmt.Println(k.Field(0).Tag) // 根据索获取字段Tag fmt.Println(k.FieldByName("Name")) // 根据名称获取字段 fmt.Println(k.FieldByIndex([]int{0})) // 根据索引链获取嵌套字段 // 引用类型需要用Elem()获取指针所指的对象类型 h := reflect.TypeOf(n).Elem() fmt.Println(h.NumMethod()) // 可访问的方法数(仅对接口类型统计) fmt.Println(h.Method(0), h.Method(0).Name) fmt.Println(h.MethodByName("A")) }
Value: 表示类型的值(不是所有Go类型的Value值都能使用所有方法,可参见每个方法的文档获取使用限制,在调用有分类限定的方法时,应先使用Kind方法获知类型的分类,调用该分类不支持的方法会导致运行时的panic)。
案例1:
package main import ( "reflect" "fmt" ) func main() { s := "www" val := reflect.ValueOf(s) fmt.Println(val.String()) // 返回值 fmt.Println(val.Kind().String()) // 返回类型 s1 := reflect.ValueOf(&s) // 获取Value类型 s1.Elem().SetString("blog") // 设置值 fmt.Println(s, s1.Elem().String()) }
案例2:
package main import ( "reflect" "fmt" ) type B struct { Name string `json:"name"` Value string `json:"value"` } func main() { s := B{Name: "www", Value: "ywfuns.com"} val := reflect.ValueOf(s) fmt.Println(val.NumField()) fmt.Println(val.Field(0)) fmt.Println(val.FieldByName("Name")) m := reflect.ValueOf(&s).Elem() m.FieldByName("Name").SetString("blog") fmt.Println(s.Name, m.Field(0)) }
案例3:
通过反射调用结构体中的方法,通过reflect.Value.Method(i int).Call()或者reflect.Value.MethodByName(name string).Call()实现
package main import ( "fmt" "reflect" ) type C struct { Name string `json:"name"` Value string `json:"value"` } func (c *C) Get() { fmt.Println("Get is", c.Name) } func (c *C) Set(s string) { c.Name = s fmt.Println("Set is ", c.Name) } func main() { s := &C{Name: "www", Value: "ywfuns"} val := reflect.ValueOf(s) // 也可以使用reflect.ValueOf(&s).Elem() val.MethodByName("Get").Call(nil) p := make([]reflect.Value, 1) p[0] = reflect.ValueOf("blog") val.MethodByName("Set").Call(p) // 通过名称调用 p[0] = reflect.ValueOf("Test") val.Method(1).Call(p) // 通过索引调用 fmt.Println(s.Name, s.Value) }