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