镜像镜像09-11 22:47

反射的使用示例

反射 reflection

  • 通过反射可以动态的调用方法
  • 反射可以通过TypeOfValueOf函数从接口中获取目标对象信息
  • 利用反射修改对象状态
  • 反射会将匿名字段作为独立字段

通过反射获取对象的类型和值

示例如下:

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id int
	Name string
	Age int
}

func (u User) Hello(){
	fmt.Println("Hello World")
}

//参数o : 可以接收任何类型
func Info(o interface{}){
	//获取参数o的类型
	t:= reflect.TypeOf(o)
	//打印类型的名称
	fmt.Printf("Type : %v\n", t.Name())

	v := reflect.ValueOf(o)
	fmt.Println("Fields:")

	//遍历类型的字段
	for i:=0;i<t.NumField();i++{
		//根据索引取得字段
		f := t.Field(i)
		//取出字段对应的值
		val := v.Field(i).Interface()
		fmt.Printf("%5s: %v = %v\n",
			f.Name, f.Type, val)
	}
}

func main() {
	u:= User{1,"Jack",22}
	Info(u)
}

打印结果:

Type : User
Fields:
   Id: int = 1
 Name: string = Jack
  Age: int = 22

获取方法信息

继续编写 Info 方法,片段如下:

	...
	//获取方法信息
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		fmt.Printf("%6s: %v\n", m.Name, m.Type)
	}

打印结果:

 Hello: func(main.User)

注意:
上述Info 方法接收的是值类型,是一个struct,如果是指针类型,就会报错,这里需要加入类型判断。

func main(){
      u:= User{1,"Jack",22}
      Info(&u)
}

报错如下:

panic: reflect: NumField of non-struct type
...

修改如下:

//参数o : 可以接收任何类型
func Info(o interface{}) {
	//获取参数o的类型
	t := reflect.TypeOf(o)
	
	//判断参数o的类型,必须是struct
	if k := t.Kind();k!= reflect.Struct{
		fmt.Println("non-struct type")
		return
	}
	...

反射结构中的嵌入字段

type Manager struct {
	User //匿名字段
	title string
}
func main() {

	m := Manager{User: User{1, "Jack", 12}, title: "Student"}
	t := reflect.TypeOf(m)

	//取出匿名字段
	fmt.Printf("%#v\n", t.Field(0))
	//取到的是匿名字段User
	fmt.Printf("%#v\n", t.FieldByIndex([]int{0}))
	//取到的是匿名字段User的第一个属性Id
	fmt.Printf("%#v\n", t.FieldByIndex([]int{0,0}))
	//取到的是Manager的第二个属性title
	fmt.Printf("%#v\n", t.FieldByIndex([]int{1}))
}
reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x10b5c80), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}
reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x10b5c80), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}
reflect.StructField{Name:"Id", PkgPath:"", Type:(*reflect.rtype)(0x10a29a0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false}
reflect.StructField{Name:"title", PkgPath:"main", Type:(*reflect.rtype)(0x10a30a0), Tag:"", Offset:0x20, Index:[]int{1}, Anonymous:false}

注意:返回的StructField的属性Anonymous ,其表示是否为匿名字段

通过反射修改基本类型的值

func main() {
	x := 123
	v := reflect.ValueOf(&x)
	v.Elem().SetInt(456)
	fmt.Println(x)
}

输出:
456

Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func main() {
	u := User{1, "Jack", 22}
	Set(&u)
	fmt.Println(u)
}

func Set(o interface{}) {

	v := reflect.ValueOf(o)

	//类型必须是Ptr和可修改的
	if v.Kind() != reflect.Ptr && !v.Elem().CanSet() {
		fmt.Println("")
		return
	} else {
		v = v.Elem()
	}

	if f:=v.FieldByName("Name");f.Kind()== reflect.String{
		f.SetString("Bob")
	}
}

输出:
{1 Bob 22}

上面的查询字段可以增加一个判断,判断字段是否有效,修改如下:

func Set(o interface{}) {

	v := reflect.ValueOf(o)

	//类型必须是Ptr和可修改的
	if v.Kind() != reflect.Ptr && !v.Elem().CanSet() {
		fmt.Println("")
		return
	} else {
		v = v.Elem()
	}

	f:=v.FieldByName("Name1")
	if !f.IsValid(){
		fmt.Println("字段无效")
		return
	}
	if f.Kind()== reflect.String{
		f.SetString("Bob")
	}
}

如何通过反射进行方法的调用

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) Hello(name string){
	fmt.Printf("Hello , %v .  My Name is %v\n", name, u.Name)
}

func main() {
	u:= User{1, "Jack",22}
	v := reflect.ValueOf(u)

	mv := v.MethodByName("Hello")

	args := []reflect.Value{reflect.ValueOf("joe")}
	mv.Call(args)
}

程序之家二维码

000
评论