golang反射(reflect)struct操作

golang反射(reflect)struct操作

1、信息获取

reflect提供了两种类型来进行访问接口变量的内容:

类型作用
ValueOf获取输入参数接口中的数据的值,如果为空则返回0 <- 注意是0
TypeOf动态获取输入参数接口中的值的类型,如果为空则返回nil <- 注意是nil

2、示例代码

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"))
	}
}

3、函数说明

3.1、val.Kind()

// 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
)

3.2、val.NumField()

// 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,该方法函数表示获取结构体中字段的数量。

3.3、typ.Field(i)

// 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.
	...
}

_3.4、_value.SetString(“Test”)

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"))
	}
}

4、方法调用

4.1、val调用

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型的切片,需要经过转换。

4.2、typ查看方法信息

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
}

4.3、typ方法调用

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})
}

5、完整示例代码

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})
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值