golang Reflect包

johnL · · 2335 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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{},若需要赋值则需要传入对象的指针,也就是值传递或址传递的意思。 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++{          //NumField()会判断Kind()是否为struct 不是的话会panic
		argType:= v.Field(i)
		if argType.Kind() == reflect.Ptr{
			if argType.Elem().Kind() == reflect.Struct{
				fmt.Println("========>")
				getStructArgProperty(argType.Elem())
			}else {
				fmt.Println(argType.Elem().Kind(),"     : ",argType ,"   ",argType.Elem().CanSet())
			}
		}else {
			if argType.Kind()==reflect.Struct {
				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方法


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:johnL

查看原文:golang Reflect包

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2335 次点击  ∙  1 赞  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传