Go 反射(reflect)

53 篇文章 0 订阅

反射可以理解为在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;Go 没有类的概念,但可以通过interface 、struct实现类似功能。

  • 通过反射可以“动态”调用方法
  • 反射可大大提高程序的灵活性,使得interface{}有更大的发挥余地

1、reflect的基本功能TypeOf和ValueOf

示例:

package main

import (
   "fmt"
   "reflect"
)

func main() {
   var name string = "jerry"
   fmt.Println("type: ", reflect.TypeOf(name))
   fmt.Println("value: ", reflect.ValueOf(name))
}

输出结果:

type: string
value: jerry

说明:

reflect.TypeOf:获取参数的type类型,如float64、int、各种pointer、struct 等类型,如果判断变量的类型是否是某种类型

示例:

if reflect.TypeOf(v).String()=="string" {
   
}

v是变量

reflect.ValueOf:获取参数的值。

2、reflect 对 struct 基本操作

示例:

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) Login() {
	fmt.Println("login")
}

func main() {
	u := User{1, "jerry", 23}
	t := reflect.TypeOf(u) //反射出一个interface{}的类型,main.User
	v := reflect.ValueOf(u)
	for i := 0; i < t.NumField(); i++ { //通过索引来取得它的所有字段,这里通过t.NumField来获取它多拥有的字段数量,同时来决定循环的次数
		f := t.Field(i)               //通过这个i作为它的索引,从0开始来取得它的字段
		val := v.Field(i).Interface() //通过interface方法来取出这个字段所对应的值
		fmt.Printf("%6s:%v =%v\n", f.Name, f.Type, val)
	}
	for i := 0; i < t.NumMethod(); i++ { //这里同样通过t.NumMethod来获取它拥有的方法的数量,来决定循环的次数
		m := t.Method(i)
		fmt.Printf("%6s:%v\n", m.Name, m.Type)
	}

	fmt.Println(t.Name())          //类型名 User
	fmt.Println(t.Kind().String()) //Type类型表示的具体分类  struct
	fmt.Println(t.PkgPath())       //反射对象所在的短包名 main
	fmt.Println(t.String())        //包名.类型名  main.User
	fmt.Println(t.Size())          //要保存一个该类型要多少个字节 32
	fmt.Println(t.Align())         //返回当从内存中申请一个该类型值时,会对齐的字节数 8
	fmt.Println(t.FieldAlign())    //返回当该类型作为结构体的字段时,会对齐的字节数 8

	fmt.Println(t.AssignableTo(reflect.TypeOf(u)))  // 如果该类型的值可以直接赋值给u代表的类型,返回真 true
	fmt.Println(t.ConvertibleTo(reflect.TypeOf(u))) // 如该类型的值可以转换为u代表的类型,返回真 true

	fmt.Println(t.NumField())             // 返回struct类型的字段数(匿名字段算作一个字段),如非结构体类型将panic  3
	fmt.Println(t.Field(0).Name)          // 返回struct类型的第i个字段的类型,如非结构体或者i不在[0, NumField())内将会panic  Id
	fmt.Println(t.FieldByName("Age"))     // 返回该类型名为name的字段(会查找匿名字段及其子字段),布尔值说明是否找到,如非结构体将panic
	fmt.Println(t.FieldByIndex([]int{0})) // 返回索引序列指定的嵌套字段的类型,等价于用索引中每个值链式调用本方法,如非结构体将会panic

	//ValueOf
	fmt.Println(v.IsValid()) //返回v是否持有值,如果v是value零值会返回假,此时v除了IsValid String Kind之外的方法都会导致panic
	fmt.Println(v.Kind())    //返回v持有值的分类,如果v是value零值,返回值为invalid  struct
	fmt.Println(v.Type())    //返回v持有值的类型Type表示  main.User

	//结构体指针
	//vv := &v
	fmt.Println(v.Convert(reflect.TypeOf(u)).FieldByName("Name")) // //转换为其他类型的值,如果无法使用标准Go转换规则来转换,那么panic  jerry
	pv := reflect.ValueOf(&u)
	pp := pv.Elem() //返回持有的接口的值,或者指针的值,如果不是interface{}或指针会panic,实际上是从 *User到User
	fmt.Println(pp)
	fmt.Println(v.FieldByName("Name").CanSet())  //是否可以设置Name的值  false
	fmt.Println(pp.FieldByName("Name").CanSet()) //是否可以设置Name的值  true
	pp.FieldByName("Name").SetString("newname")  //设置Name的值
	fmt.Println(u)                               //{1 newname 23}

	fmt.Println(pp.FieldByName("Name").Interface()) //把Name当做interface{}值  newname
	fmt.Println(pp.FieldByName("Name").String())    //返回v持有的值的字符串表示,如果v的值不是string也不会panic  newname
	var x int64
	fmt.Println(v.FieldByName("Age").OverflowInt(x)) //如果v持有值的类型不能溢出的表示x,会返回真,如果v的kind不是int int8-int64会panic false
}

通过反射设置结构体的字段值,字段名称要大写,如 Name 不能用 name

3、通过reflect(反射)可以动态调用结构体方法

package main

import (
   "fmt"
   "reflect"
)

type User struct {
   Id   int
   Name string
   Age  int
}

func (u User) Login(name string) {
   fmt.Println("login" + name)
}
func (u User) LoginOut(name, name1 string) {
   fmt.Println("loginout" + name)
   fmt.Println("loginout" + name1)
}

func main() {
   user := User{Id: 1, Name: "jerry", Age: 29}
   val := reflect.ValueOf(&user) //获取Value类型,也可以使用reflect.ValueOf(&user).Elem()

   params := make([]reflect.Value, 1)
   params[0] = reflect.ValueOf("herry")
   val.MethodByName("Login").Call(params) //通过名称调用方法
   paramstwo := make([]reflect.Value, 2)
   paramstwo[0] = reflect.ValueOf("herry")
   paramstwo[1] = reflect.ValueOf("jack")
   fmt.Println(params)
   val.Method(1).Call(paramstwo) //通过方法索引调用,paramstwo 含有两个参数

}

links

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值