赞
踩
reflect提供了两种类型来进行访问接口变量的内容:
类型 | 作用 |
---|---|
ValueOf | 获取输入参数接口中的数据的值,如果为空则返回0 <- 注意是0 |
TypeOf | 动态获取输入参数接口中的值的类型,如果为空则返回nil <- 注意是nil |
package main import ( "fmt" "reflect" ) type Person struct { Name string `json:"name"` Count int } func main() { test(Person{ Name: "lei", Count: 2, }) } func test(body interface{}) { //TypeOf会返回目标数据的类型,比如int/float/struct/指针等 typ := reflect.TypeOf(body) //ValueOf返回目标数据的的值 val := reflect.ValueOf(body) if val.Kind() != reflect.Struct { fmt.Println("expect struct") return } fmt.Println(typ) fmt.Println(val) for i := 0; i < val.NumField(); i++ { field := typ.Field(i) //字段的数据类型 value := val.Field(i) //字段的数据值 fmt.Println("type1:", field) //type: {Name string json:"name" 0 [0] false} fmt.Println("value1:", value) //value: lei switch value.Kind() { case reflect.Int: value.SetInt(88) //往该字段设值 case reflect.String: //这里存在一个问题无法对字段进行值修改,panic:reflect: reflect.flag.mustBeAssignable using unaddressable value,后文介绍解决 value.SetString("Test") // 往该字段设值 default: fmt.Println("类型不支持") } fmt.Println("type2:", field) //type: {Name string json:"name" 0 [0] false} fmt.Println("value2:", value) //value: Test tag := field.Tag fmt.Println(tag.Get("json")) } }
// Kind returns v's Kind.
// If v is the zero Value (IsValid returns false), Kind returns Invalid.
func (v Value) Kind() Kind {
return v.kind()
}
返回值为Kind,表示golang语言自身定义的基本类型:
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 )
// NumField returns the number of fields in the struct v.
// It panics if v's Kind is not Struct.
func (v Value) NumField() int {
v.mustBe(Struct)
tt := (*structType)(unsafe.Pointer(v.typ))
return len(tt.fields)
}
返回值为int,v.mustBe(Struct)
值类型必须为一个struct,该方法函数表示获取结构体中字段的数量。
// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
type Type interface {
Field(i int) StructField
}
反射的类型接口,该接口实现了获取指定结构体参数当中的某下标 i
的字段信息:
// A StructField describes a single field in a struct.
type StructField struct {
// Name is the field name.
Name string
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
PkgPath string
Type Type // field type
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
}
field.Tag
是取到该字段标签信息 StructTag
,它又具备两个对外暴露的方法:
// 只是复用了Lookup,只不过忽略了标签存在的说明,不存在返回“”
func (tag StructTag) Get(key string) string {
v, _ := tag.Lookup(key)
return v
}
// 返回标签对应的值与标签是否存在的说明
func (tag StructTag) Lookup(key string) (value string, ok bool) {
// When modifying this code, also update the validateStructTag code
// in cmd/vet/structtag.go.
...
}
reflect包提供了方法对Value实例中的字段值做出修改,值得注意的是,根据官方描述,为了改变一个反射对象,其值必需是可修改的,这里的可修改值得注意,通过输入的接口初始化的Value实例,返回一个根据输入的接口中存储的值初始化的新Value类型(跟按值传递参数一致),因此,对Value中的值做出修改并没有改变外部接口值,因此并不支持value.SetString("Test")
错误信息:panic: reflect: reflect.flag.mustBeAssignable using unaddressable value
因此如果对于值的修改要以指针传递参数&Person
形式,可使用Elem()函数将用指针量初始化的Value实例中实际存储的值(相当于引用传递,这里的Elem()相当于一个解引用)
修改后的示例代码:
package main import ( "fmt" "reflect" ) type Person struct { Name string `json:"name"` Count int } func (p *Person) Print() { fmt.Println("print:",p) } func (p *Person) CountAdd(num int) int { return p.Count + num } func main() { test(&Person{ Name: "lei", Count: 2, }) } func test(body interface{}) { //TypeOf会返回目标数据的类型,比如int/float/struct/指针等 typ := reflect.TypeOf(body) //ValueOf返回目标数据的的值 val := reflect.ValueOf(body) if val.Elem().Kind() != reflect.Struct { fmt.Println("expect struct") return } fmt.Println(typ) fmt.Println(val) for i := 0; i < val.Elem().NumField(); i++ { field := typ.Elem().Field(i) //字段的数据类型 value := val.Elem().Field(i) //字段的数据值 fmt.Println("type1:", field) //type: {Name string json:"name" 0 [0] false} fmt.Println("value1:", value) //value: lei switch value.Kind() { case reflect.Int: value.SetInt(88) //往该字段设值 case reflect.String: value.SetString("Test") // 往该字段设值 default: fmt.Println("类型不支持") } fmt.Println("type2:", field) //type: {Name string json:"name" 0 [0] false} fmt.Println("value2:", value) //value: Test fmt.Println(field.Tag.Get("json")) } }
call := val.Method(0).Call([]reflect.Value{reflect.ValueOf(2)}) //调用CountAdd方法
fmt.Println("返回值:", call[0]) //返回值: 90
val.MethodByName("Print").Call(nil) //通过方法名调用
除解析一个接口的结构字段和方法外,还可以对注册在结构上的方法进行调用,调用过程中需要注意接收者是否为指针型,被调用函数应当是被导出类型,调用过程只能在包含了结构实例值的Value类型上使用,type类型无法使用,func (v Value) Call(in []Value) []Value
, 参数和返回值都是reflect包中Value型的切片,需要经过转换。
for i := 0; i < typ.NumMethod(); i++ {
method := typ.Method(i)
fmt.Println(method.Name,method.Type) //CountAdd func(*main.Person, int) int
}
typ.Method(i)
其返回值包含该指向方法的基本信息:
// Method represents a single method.
type Method struct {
// Name is the method name.
// PkgPath is the package path that qualifies a lower case (unexported)
// method name. It is empty for upper case (exported) method names.
// The combination of PkgPath and Name uniquely identifies a method
// in a method set.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
Name string
PkgPath string
Type Type // method type
Func Value // func with receiver as first argument
Index int // index for Type.Method
}
for i := 0; i < typ.NumMethod(); i++ {
method := typ.Method(i)
fmt.Println(method.Name,method.Type) //CountAdd func(*main.Person, int) int
if method.Name == "CountAdd" {
//对于Type类型包含的方法,因为没有实际值作为接收者,需要吧传入的第一个参数作为接收者
retInfo := method.Func.Call([]reflect.Value{val,reflect.ValueOf(2)})
fmt.Println("返回值:", retInfo[0])
}
}
method, ok := typ.MethodByName("Print")
if ok {
method.Func.Call([]reflect.Value{val})
}
package main import ( "fmt" "reflect" ) type Person struct { Name string `json:"name"` Count int } func (p *Person) Print() { fmt.Println("print:", p) //print: &{Test 88} } func (p *Person) CountAdd(num int) int { return p.Count + num } func main() { test(&Person{ Name: "lei", Count: 2, }) } func test(body interface{}) { //TypeOf会返回目标数据的类型,比如int/float/struct/指针等 typ := reflect.TypeOf(body) //ValueOf返回目标数据的的值 val := reflect.ValueOf(body) if val.Elem().Kind() != reflect.Struct { fmt.Println("expect struct") return } fmt.Println(typ) fmt.Println(val) for i := 0; i < val.Elem().NumField(); i++ { field := typ.Elem().Field(i) //字段的数据类型 value := val.Elem().Field(i) //字段的数据值 fmt.Println("type1:", field) //type: {Name string json:"name" 0 [0] false} fmt.Println("value1:", value) //value: lei switch value.Kind() { case reflect.Int: value.SetInt(88) //往该字段设值 case reflect.String: value.SetString("Test") // 往该字段设值 default: fmt.Println("类型不支持") } fmt.Println("type2:", field) //type: {Name string json:"name" 0 [0] false} fmt.Println("value2:", value) //value: Test fmt.Println(field.Tag.Get("json")) } // 除解析一个接口的结构字段和方法外,还可以对注册在结构上的方法进行调用 // 调用过程中需要注意接收者是否为指针型,被调用函数应当是被导出类型 // 这个调用过程只能在包含了结构实例值的Value类型上使用,Type类型无法使用 // func (v Value) Call(in []Value) []Value // 参数和返回值都是reflect包中Value型的切片,需要经过转换 call := val.Method(0).Call([]reflect.Value{reflect.ValueOf(2)}) //调用CountAdd方法 fmt.Println("返回值:", call[0]) //返回值: 90 val.MethodByName("Print").Call(nil) //通过方法名调用 for i := 0; i < typ.NumMethod(); i++ { method := typ.Method(i) fmt.Println(method.Name,method.Type) //CountAdd func(*main.Person, int) int if method.Name == "CountAdd" { //对于Type类型包含的方法,因为没有实际值作为接收者,需要吧传入的第一个参数作为接收者 retInfo := method.Func.Call([]reflect.Value{val,reflect.ValueOf(2)}) fmt.Println("返回值:", retInfo[0]) } } method, ok := typ.MethodByName("Print") if ok { method.Func.Call([]reflect.Value{val}) } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。