go reflect struct 遍历,反射

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)

The Laws of Reflection

http://studygolang.com/articles/1251

转载于:https://my.oschina.net/solate/blog/715681

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值