reflect 打印属性
- package main
-
- import (
- "fmt"
- "reflect"
- )
-
- type User struct {
- Id int `json:"id"`
- Name string `json:"name"`
- addr string `json:"_"`
- }
-
- func main(){
- u := User{Id:1001, Name:"aaa" , addr:"bbb"}
- t := reflect.TypeOf(u)
- v := reflect.ValueOf(u)
- for i := 0; i < v.NumField(); i++ {
- if v.Field(i).CanInterface() { //判断是否为可导出字段
- fmt.Printf("%s %s = %v -tag:%s \n",
- t.Field(i).Name,
- t.Field(i).Type,
- v.Field(i).Interface(),
- t.Field(i).Tag)
- }
- }
-
-
- }
-
- //结果
- Id int = 1001 -tag:json:"id"
- Name string = aaa -tag:json:"name"
-
注:当结构体中含有非导出字段时,v.Field(i).Interface()会panic,
所以使用v.Field(i).CanInterface()先判断一下
struct 嵌套结构遍历
- package main
-
- import (
- "reflect"
- "fmt"
- )
-
- type User struct {
- Id int
- Name string
- Address Address
- }
-
- type Address struct {
- Add string
- Res int
- }
-
- func main(){
- u := User{Id:1001, Name:"aaa" , Address:Address{Add:"ccccccccc", Res:12}}
- t := reflect.TypeOf(u)
- v := reflect.ValueOf(u)
- for i := 0; i < v.NumField(); i++ {
- if v.Field(i).CanInterface() { //判断是否为可导出字段
-
- //判断是否是嵌套结构
- if v.Field(i).Type().Kind() == reflect.Struct{
- structField := v.Field(i).Type()
- for j :=0 ; j< structField.NumField(); j++ {
- fmt.Printf("%s %s = %v -tag:%s \n",
- structField.Field(j).Name,
- structField.Field(j).Type,
- v.Field(i).Field(j).Interface(),
- structField.Field(j).Tag)
- }
- continue
- }
-
- fmt.Printf("%s %s = %v -tag:%s \n",
- t.Field(i).Name,
- t.Field(i).Type,
- v.Field(i).Interface(),
- t.Field(i).Tag)
- }
-
- }
-
-
- }
-
- //结果
- Id int = 1001 -tag:
- Name string = aaa -tag:
- Add string = ccccccccc -tag:
- Res int = 12 -tag:
-
根据名字获取 嵌套结构属性值
func Field(i int) StructField //使用索引来访问字段,索引从0开始,如果越界将panic
func FieldByName(name string) (StructField,bool) //使用名称来访问字段,如果未找到那么返回false
func FieldByNameFunc(match func(string) bool) (StructField,bool) //访问名称使得match函数返回true的字段,在同一个内嵌层次上,只能有一个字段使得match返回true。如果同一层次上多个字段使得match返回true,那么这些字段都认为是不符合要求的
func FieldByIndex(index []int) StructField //这个方法使得访问结构的内嵌字段成为可能。将访问各个层次的字段的索引排列起来,就形成了一个[]int,参数index不可越界,否则panic
- package main
-
- import (
- "reflect"
- "fmt"
- )
-
- type Test struct {
- Name string
- User User
- }
- type User struct {
- Age int
- Name string
- }
- var test Test = Test{
- Name:"aaa",
- User:User{
- Name:"bbb",
- Age:12,
- },
- }
-
-
- func main() {
- sysConfig := reflect.ValueOf(&test).Elem()
- fmt.Println(sysConfig.FieldByIndex([]int{1})) //打印User层属性
- //fmt.Println(sysConfig.FieldByName("Age")) //直接打印下一层不成功
- flag := sysConfig.FieldByName("Age") == reflect.Value{}
- if flag {
- fmt.Println(sysConfig.FieldByIndex([]int{1}).FieldByName("Age")) //12
- }
-
- }
-
-
当再加一层, FieldByIndex([]int{1, 2}), 1和2都要加上才可以,直接就写2不行。第0层要使用其他的field方法。
- package main
-
- import (
- "reflect"
- "fmt"
- )
-
- type Test struct {
- Name string
- User User
- }
- type User struct {
- Age int
- Name string
- Addr Addr
- }
- type Addr struct {
- Address string
- }
- var test Test = Test{
- Name:"aaa",
- User:User{
- Name:"bbb",
- Age:12,
- Addr:Addr{
- Address:"dddddd",
- },
- },
- }
-
-
- func main() {
- var val reflect.Value
- sysConfig := reflect.ValueOf(&test).Elem()
- fmt.Println(sysConfig.FieldByIndex([]int{1, 2})) //Addr
- //fmt.Println(sysConfig.FieldByName("Age"))
- flag := sysConfig.FieldByName("Address") == val
- if flag {
- fmt.Println(sysConfig.FieldByIndex([]int{1, 2}).FieldByName("Address")) //ddddd
- }
-
- }
反射使用情况总结
1.反射只能反射单层结构,如果struct又包含了一个struct那么,需要先遍历前面这个,获取后面struct的指针活元素才能遍历。
- type Test struct {
- Name string
- Test2 Test2
- }
- type Test2 struct {
- Add string
- }
-
- //遍历Test 获得Test2指针或者元素才能遍历Test2
reflect 打印方法
-
打印方法最大的问题是方法有指针方法和struct方法之分, 也就是在写这个
func (m *M) Pri()
加不加*
的问题. -
这个再调用的时候感觉是差不多的,但是反射的时候差别很大,在获取 NumMethod() 时获得的数量是不同的.(在下面"相似方法"进行详细描述)
-
指针方法和struct方法 类似于 Java中的 对象方法 和 类方法 。指针方法类同与对象方法,只有是指针的时候才能调用。
详细分析
TypeOf 常用方法
- Name() string : 返回类型名称
- PkgPath() string : 返回类型所在包的路径。
- Kind() Kind: 返回 Type 的类型,是下列值之一。
- const (
- Invalid Kind = iota
- Bool
- Int
- Int8
- Int16
- Int32
- Int64
- Uint
- Uint8
- Uint16
- Uint32
- Uint64
- Uintptr
- Float32
- Float64
- Complex64
- Complex128
- Array
- Chan
- Func
- Interface
- Map
- Ptr
- Slice
- String
- Struct
- UnsafePointer
- )
-
- NumMethod() int:返回类型的函数个数。
- Method(n int) Method:返回类型的第 n 个函数。
- MethodByName(string) (Method, bool) : 根据名称返回 Type 的指定函数。
- NumOut() int:返回函数类型的返回值的数量,如果不是函数类型,将会产生一个错误。
- Out(i int) Type:返回函数类型的第 i 个返回值,如果不是函数类型,将产生一个错误。
- NumIn() int:返回函数类型的输入参数数量。
- In(i int) Type:返回函数类型的第 i 个输入参数,如果不是函数类型将产生一个错误。
- Elem() Type:通常在我们反射的对像是指针类型时,使用该函数返回指针所指向的对像的类型。
- NumField() int:返回结构体类型的字段数量。
- Field(i int) StructField:返回结构体类型的第 i 个字段。
- FieldByName(name string) (StructField, bool):按名称返回结构体的字段
相似方法辨析
Kind() : 一共27个反射类别
//底层都是 Kind(f & (1 << 5 -1))所以结果相同, 因为2进制31为 00011111 验证 27 个种类的位数正好是这么多.
** 但是不是很清楚28的情况怎么处理不越界 **, 懂了后补充.
- // TypeOf
- func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }
- kindMask = (1 << 5) - 1
-
- // ValueOf
- const (
- flagKindWidth = 5 // there are 27 kinds
- flagKindMask flag = 1<<flagKindWidth - 1
- )
- func (f flag) kind() Kind {
- return Kind(f & flagKindMask)
- }
-
NumMethod()
获取方法数量,当传入的不是指针的时候, 只能获得struct方法,而不能获得指针方法.
- package main
-
- import (
- "fmt"
- "reflect"
- )
-
- type MyStruct struct{
- name string
- }
-
- func (this *MyStruct)GetName() string {
- return this.name
- }
-
- func (this MyStruct) Pri() {
- fmt.Println(this)
- }
-
- func main() {
- s := MyStruct{name:"cc"}
-
- t := reflect.TypeOf(s)
- v := reflect.ValueOf(s)
-
- //当传入的是struct
- fmt.Println(t.NumMethod()) // 1
- fmt.Println(v.NumMethod()) // 1
-
-
- s1 := &MyStruct{name:"cc"} //这里等同于 new(MyStruct), 传入指针
-
- t1 := reflect.TypeOf(s1) //如果是 s 那么这里要传 &s
- v1 := reflect.ValueOf(s1)
-
- //传入的是指针, 所以指针方法和struct方法类似
- fmt.Println(t1.NumMethod()) // 2
- fmt.Println(v1.NumMethod()) // 2
-
- //typeOf 提供了将struct变成指针的方法
- t = reflect.PtrTo(t) //将t变为指向t的指针
- fmt.Println(t.NumMethod()) // 2
- fmt.Println(v.NumMethod()) // 1
-
-
- //这种是将指针变成struct的方法
- if t.Kind() == reflect.Ptr {
- t = t.Elem() //将t变成指向的对象
- fmt.Println("-----------")
- }
-
- fmt.Println(t.NumMethod()) // 1
- fmt.Println(v.NumMethod()) // 1
-
-
- //value 有将指针变成 struct的方法, 没有struct变成指针的方法
- if v1.Kind() == reflect.Ptr {
- v1 = v1.Elem() //将t变成指向的对象
- fmt.Println("-----------")
- }
- //t1 依然是指针, 但是v1已经变成struct了
- fmt.Println(t1.NumMethod()) // 2
- fmt.Println(v1.NumMethod()) // 1
-
- }
-
Go语言中反射包的实现原理(The Laws of Reflection)
http://studygolang.com/articles/1251