golang Reflect包

六月ゝ 毕业季﹏ 提交于 2019-12-02 22:45:20

Reflect包

Reflect 反射包有2个重要的类型,分别通过Typeof()ValueOf()返回。 分别在源码包里的reflect包中的type.govalue.go

Type

TypeOf() 返回一个Type接口类型,源码中

type Type interface {
	Align() int
	FieldAlign() int
	Method(int) Method
	MethodByName(string) (Method, bool)
	NumMethod() int
	Name() string
	PkgPath() string
	Size() uintptr
	String() string
	Kind() Kind
	Implements(u Type) bool
	ConvertibleTo(u Type) bool
	Comparable() bool
	Bits() int
	ChanDir() ChanDir
	IsVariadic() bool
	Elem() Type
	Field(i int) StructField
	FieldByIndex(index []int) StructField
	FieldByName(name string) (StructField, bool)
	FieldByNameFunc(match func(string) bool) (StructField, bool)
	In(i int) Type
	Key() Type
	Len() int
	NumField() int
	NumIn() int
	NumOut() int
	Out(i int) Type
	common() *rtype
	uncommon() *uncommonType
}

有一个rtype结构体 实现了Type接口的所有方法。源码:

type rtype struct {
	size       uintptr
	ptrdata    uintptr  
	hash       uint32  
	tflag      tflag    
	align      uint8    
	fieldAlign uint8    
	kind       uint8   
	alg        *typeAlg 
	gcdata     *byte    
	str        nameOff 
	ptrToThis  typeOff 
}

TypeOf会返回一个rtype。可以调用他的方法

例如:

argTest := "test"
v :=reflect.TypeOf(argTest)

fmt.Println(v.Kind()) //string

argTest1 :=&testStruct{}
v1 :=reflect.TypeOf(argTest1)

fmt.Println(v1.Kind()) //  ptr


argTest1 :=&testStruct{}
v1 :=reflect.TypeOf(*argTest1)

fmt.Println(v1.Kind()) //struct

argTest1 :=&testStruct{}
v1 :=reflect.TypeOf(argTest1).Elem()

fmt.Println(v1.Kind()) // struct

可以利用Kind()方法来获取反射对象的类型,如果是struct类型传入的是一个地址,会得到ptr类型,可以传入指向地址的值或者利用Elem()方法可以得到对应的类型。所有基础类型的基本属性都可以通过rtype来获得。基础类型可以查看type.goKind类型中的包含。

获取结构体中所有元素的属性。

func getStructArgProperty(t interface{}){
	var v reflect.Type
	if  reflect.TypeOf(t).Kind() == reflect.Ptr {      //
		if reflect.TypeOf(t).Elem().Kind() != reflect.Struct{
			fmt.Println("不是结构体")
			return
		}
		v =reflect.TypeOf(t).Elem()
	}else{
		if reflect.TypeOf(t).Kind() != reflect.Struct{
			fmt.Println("不是结构体")
			return
		}
		v=reflect.TypeOf(t)
	}
	run(v)
}
func run(v reflect.Type){
	for  i:=0;i<v.NumField();i++{
		argType:= v.Field(i)
		if argType.Type.Kind() ==reflect.Ptr {
			fmt.Println(argType.Name,argType.Type.Elem().Kind())
		}else {
			if argType.Type.Kind() ==reflect.Struct {
				fmt.Println("   =====>",argType.Name)
				run(argType.Type)
			}else {
				fmt.Println(argType.Name, argType.Type.Kind())
			}
		}
	}
}

但若要取到对象的值,则需要用到ValueOf。

Value

ValueOf() 返回一个Value结构体类型,源码中

type Value struct {
	typ *rtype
	ptr unsafe.Pointer
	flag
}

rtypeKind()不同的是,其中flag 是一个uintptr类型,实现了kind()方法。新增了类型,源码中

const (
	flagKindWidth        = 5 // there are 27 kinds
	flagKindMask    flag = 1<<flagKindWidth - 1
	flagStickyRO    flag = 1 << 5
	flagEmbedRO     flag = 1 << 6
	flagIndir       flag = 1 << 7
	flagAddr        flag = 1 << 8
	flagMethod      flag = 1 << 9
	flagMethodShift      = 10
	flagRO          flag = flagStickyRO | flagEmbedRO
)

利用 ValueOf 取值,赋值

arr := [...]int{1,2,3,4}
v := reflect.ValueOf(arr)
fmt.Println(v)  //[1,2,3,4]

v1 := reflect.ValueOf(&arr)
fmt.Println(v1) //&[1,2,3,4]

fmt.Println(v.Elem().CanSet())    // panic
fmt.Println(v1.Elem().CanSet())   // true

v1.Elem().Index(0).SetInt(10)
fmt.Println(arr)  // 10,2,3,4

Elem()方法只区分了interface{} ptr,再处理指针类型的时候需先调用Elem()方法得到一个具体的基础类型。可以利用Kind()方法来得知ValueOf返回的是指针还是interfaec{}或利用Indirect()方法来判断。,若需要赋值则需要传入对象的指针,也就是值传递或址传递的意思。 struct的取值,赋值只是调用了不同方法。例如:



type student struct{
    numb int
    name string
    Age int
    class *class
}
type class struct{
    classNumber int
    className string
}

func structValueOf(){
s := student{numb:1,name:"john",Age:18,class:&class{classNumber:1}}
v := reflect.ValueOf(&s)
getStructArgProperty(v)
}

func getStructArgProperty(v reflect.Value){
	for  i:=0;i<v.NumField();i++{
		argType:= reflect.Indirect(v.Field(i))
		if argType.Kind()==reflect.Struct {
                    fmt.Println("================>")
		    getStructArgProperty(argType)
		}else {
			if argType.CanSet() == true && argType.Kind() == reflect.Int {
				argType.SetInt(10)
			}
			fmt.Println(argType.Kind(), "     : ", argType, "   ", argType.CanSet())
		}
	}
}

在需要修改的字段结构体的属性应该为公开。

类型的方法

若要获取类型的方法,使用TypeOf(),ValueOf()2中类型都可以获取。

不同的是TypeOf()返回方法的基本属性,但并自己没有现实调用方法,而是通过调用ValueOfCall(),而ValueOf则没有返回方法的名字等基本属性

type myType int

func (my *myType) Hi(){
	fmt.Println("my value ",*my)
}
func (my *myType) Set(x int){
	*my = myType(x)
}
func (my myType) Get() int{
	fmt.Println("my value ", my)
	return int(my)
}

var s myType = 1
v := reflect.ValueOf(&s)
v1 := reflect.TypeOf(s)

fmt.Println(" v  ",v.NumMethod())   //3
fmt.Println(" v1  ",v1.NumMethod())   //1  传入的如果是值类型,则只返回值类型方法


for i:=0;i<v1.NumMethod();i++{
	fmt.Println(v1.Method(i))   //方法名等结果,根据首字母排序
}

for i:=0;i<v.NumMethod();i++{
	fmt.Println(v.Method(i))    //reflect方法对象。
}


var para []reflect.Value
para = append(para,reflect.ValueOf(11))
fmt.Println(v.Method(2).Call(para))   //调用Set方法

para  = append(para,reflect.ValueOf(&s))
fmt.Println(v1.Method(0).Func.Call(para[1:]))  //调用Get方法

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!