11 Golang反射

反射介绍

反射可以再运行时动态获取变量信息,并修改变量内容。是实现动态编程、编写框架的关键。

Go程序在运行期使用reflect包访问程序的反射信息

reflect包

go语言的反射机制中,任何借口都是由一个具体类型(reflect.Type)和具体类型的值(reflect.Value)两部分组成。reflect包还提供了 reflect.TypeOf()和 reflect.Valueof两个函数。来获取任意对象的Value 和 Type
示例代码

func typeValueTest(){
	x := 10
	t := reflect.TypeOf(x)
	v := reflect.ValueOf(x)
	
	fmt.Printf("type:=%v value=%v \n",t,v)//type:=int value=10
}

反射中类型分为两种:Type 和Kind ,go 中我们可以使用type 关键字构造很多自定义类型,king是指的底层的类型,当需要区分指针结构体等大品种类型时使用Kind.
示例代码

func test01(){
	type myInt int64
	var a *float32 //指针
	var b myInt   //自定义类型
	var c rune    //类型别名

	t := reflect.TypeOf(a)
	//type:=  kind=ptr
	fmt.Printf("type:=%v  kind=%v \n",t.Name(),t.Kind())

	t = reflect.TypeOf(b)
	//type:=myInt  kind=int64
	fmt.Printf("type:=%v  kind=%v  \n",t.Name(),t.Kind())

	t = reflect.TypeOf(c)
	//type:=int32  kind=int32
	fmt.Printf("type:=%v  kind=%v  \n",t.Name(),t.Kind())

	type person struct{
		name string
		age int
	}
	type book struct{
		title string
	}
	var d = person{
		name:"tom",
		age:19,
	}
	var e = book{title:"booktitle",}
	t = reflect.TypeOf(d)
	//type:=person  kind=struct
	fmt.Printf("type:=%v  kind=%v  \n",t.Name(),t.Kind())

	t = reflect.TypeOf(e)
	//type:=book  kind=struct
	fmt.Printf("type:=%v  kind=%v  \n",t.Name(),t.Kind())

}

go中数组 切片 map 指针等类型的变量,他们的类型的Name方法都返回空,
go 中定义的Kind 类型清单如下:

type Kind uint
const (
    Invalid Kind = iota  // 非法类型
    Bool                 // 布尔型
    Int                  // 有符号整型
    Int8                 // 有符号8位整型
    Int16                // 有符号16位整型
    Int32                // 有符号32位整型
    Int64                // 有符号64位整型
    Uint                 // 无符号整型
    Uint8                // 无符号8位整型
    Uint16               // 无符号16位整型
    Uint32               // 无符号32位整型
    Uint64               // 无符号64位整型
    Uintptr              // 指针
    Float32              // 单精度浮点数
    Float64              // 双精度浮点数
    Complex64            // 64位复数类型
    Complex128           // 128位复数类型
    Array                // 数组
    Chan                 // 通道
    Func                 // 函数
    Interface            // 接口
    Map                  // 映射
    Ptr                  // 指针
    Slice                // 切片
    String               // 字符串
    Struct               // 结构体
    UnsafePointer        // 底层指针
)

反射中的reflect.Value类型中包含了原始值的信息,reflect.Value 和原始值可以互换。reflect.Value 提供的方法如下:

Interface() interface {} 将值以 interface{} 类型返回,可以通过类型断言转换为指定类型
Int() int64 将值以 int 类型返回,所有有符号整型均可以此方式返回
Uint() uint64 将值以 uint 类型返回,所有无符号整型均可以此方式返回
Float() float64 将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回
Bool() bool 将值以 bool 类型返回
Bytes() []bytes 将值以字节数组 []bytes 类型返回
String() string 将值以字符串类型返回
获取变量值示例

func test02(){
	var a float32 = 3.12
	var b int64 = 123
	reflectValue(a)//type is Float32 value = 3.120000
	reflectValue(b)//type is int64 value = 123
	c := reflect.ValueOf(10)
	fmt.Printf("type is %T \n",c)//type is reflect.Value
}
func reflectValue(x interface{}){
	v := reflect.ValueOf(x)
	k :=v.Kind();
	switch k{
	case reflect.Int64:
		//v.Int() 获取到原始值,然后强转成 int 64
		fmt.Printf("type is int64 value = %d \n",int64(v.Int()))
	case reflect.Float64:
		//v.Int() 获取到原始值,然后强转
		fmt.Printf("type is Flaot64 value = %f \n",float64(v.Float()))
	case reflect.Float32:
		//v.Int() 获取到原始值,然后强转
		fmt.Printf("type is Float32 value = %f \n",float32(v.Float()))
	} 
}

设置变量值
想要在函数中通过反射修改变量值,必须传地址。反射中使用 Elem()来获取指针对应的值

func test03(){
	var a int64 = 100
	//setValue1(a)//报异常
	//fmt.Println(a)
	setValue2(&a)//必须传指针才能赋值
	fmt.Println(a)//200
	var bb int =10
	//v := reflect.ValueOf(bb)
	//v.SetInt(20)//panic: reflect: reflect.Value.SetInt using unaddressable value
	
	v := reflect.ValueOf(&bb)//这样修改值才正确
	v.Elem().SetInt(20)
	fmt.Println(bb)//20
}
func setValue1(x interface{}){
	v := reflect.ValueOf(x)
	if(v.Kind() == reflect.Int64){
		v.SetInt(200)//panic: reflect: reflect.Value.SetInt using unaddressable value
	}
}
func setValue2(x interface{}){
	v := reflect.ValueOf(x)
	if(v.Elem().Kind() == reflect.Int64){
		v.Elem().SetInt(200)
	}
}

reflect.Value 的 IsNil 方法:判断当前value持有的值是否为nil,value 只有的值必须是通道、函数、接口、映射、指针、切片之一,否则报错。常被用于判断指针是否为空
reflect.Value 的 IsValid 方法:判断当前Vlaue 是否持有一个值,常被用于判断返回值是否有效

func test04(){
	var a *int
	//var a *int is NIl true
	fmt.Println("var a *int is NIl",reflect.ValueOf(a).IsNil())
	//Nil isValid  false
	fmt.Println("Nil isValid ",reflect.ValueOf(nil).IsValid())
	b := struct{}{}
	//不存在的结构体成员: false
	fmt.Println("不存在的结构体成员:",reflect.ValueOf(b).FieldByName("abc").IsValid())
	//不存在的结构体方法: false
	fmt.Println("不存在的结构体方法:",reflect.ValueOf(b).MethodByName("abc").IsValid())
	c:=map[string]int{}
	//map中不存在的键: false
	fmt.Println("map中不存在的键:",reflect.ValueOf(c).MapIndex(reflect.ValueOf("nazha")).IsValid())
}

结构体反射

相关方法
结构体对象的 reflect.Type对象可以通过 NumFiled() 和 Filed()方法获得结构体成员的详细信息
reflect.Type 中与结构体相关的方法:
Field(i int) StructField 根据索引,返回索引对应的结构体字段的信息。
NumField() int 返回结构体成员字段数量。
FieldByName(name string) (StructField, bool) 根据给定字符串返回字符串对应的结构体字段的信息。
FieldByIndex(index []int) StructField 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool) 根据传入的匹配函数匹配需要的字段。
NumMethod() int 返回该类型的方法集中方法的数目
Method(int) Method 返回该类型方法集中的第i个方法
MethodByName(string)(Method, bool) 根据方法名返回该类型方法集中的方法

StructField
用来描述结构体中的一个字段的信息,定义如下:

type StructField struct {
    // Name是字段的名字。PkgPath是非导出字段的包路径,对导出字段该字段为""。
    // 参见http://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string
    PkgPath string
    Type      Type      // 字段的类型
    Tag       StructTag // 字段的标签
    Offset    uintptr   // 字段在结构体中的字节偏移量
    Index     []int     // 用于Type.FieldByIndex时的索引切片
    Anonymous bool      // 是否匿名字段
}

示例代码


```css
type student struct{
	Name string `json:"name"`
	Score int `json:"score"`
}
func(s student) Sleep(){
	fmt.Println("shuijiao。。。。")
}
func(s student) Study(){
	fmt.Println("study.....")
}
func test05(){
	stu1:=student{
		Name:"tom",
		Score:91,
	}
	t:=reflect.TypeOf(stu1)
	fmt.Println(t.Name(),t.Kind())
	for i:=0;i<t.NumField();i++{
		field:=t.Field(i)
		fmt.Printf("name:%s,index:%d,type:%v,json tag:%v \n",field.Name,field.Index,field.Type,field.Tag.Get("json"))
	}

	v:=reflect.ValueOf(t)

	for i:=0;i<v.NumMethod();i++{
		methodType:=v.Method(i).Type
		fmt.Printf("method name :%s  type:%s \n",t.Method(i).Name,methodType)
		//var args = []reflect.Value{}
		v.Method(i).Call(nil)//调用方法
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

catch that elf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值