Golang反射

基本介绍

基本介绍

  • 在Go中,反射(reflection)是一种机制,其允许程序在运行时检查并操作变量、类型和结构的信息,而不需要提前知道它们的具体定义,使得代码更加灵活和通用。
  • 反射通常用于动态获取获取类型信息、动态创建对象、动态调用函数、动态修改对象等,在实现反射时需要用到reflect包。
  • 需要注意的是,虽然反射的功能强大,但由于其使用了运行时的类型检查和动态调用,在性能上可能会有一定的开销,因此在性能敏感的场景中,应该尽量避免过度依赖反射来实现常规的编程任务。

reflect包

reflect.Type

reflect.Type

  • reflect.Type是reflect包中的一个接口类型,用于表示任意变量的类型信息。
  • 通过reflect包中的TypeOf函数,可以获取指定变量的类型信息。

reflect.TypeOf函数的函数原型如下:

func TypeOf(i interface{
   }) Type

reflect.Type接口中常用的方法如下:

方法名 功能
Kind 获取该类型对应的Kind
Size 获取该类型的大小
Elem 获取该类型的元素的Type
NumField 获取结构体类型的字段数
NumMethod 获取该类型所绑定的方法数
Field 获取结构体类型的第i个字段的信息
Method 获取该类型所绑定的第i个方法的信息
FieldByName 获取结构体类型的字段中,指定字段名的字段信息
MethodByName 获取该类型所绑定的方法中,指定方法名的方法信息
NumIn 获取函数/方法类型的参数个数
In 获取函数/方法类型的第i个参数的Type
NumOut 获取函数/方法类型的返回值个数
Out 获取函数/方法类型的第i个返回值的Type

说明一下:

  • reflect.Type接口中的方法不需要用户手动实现,这些方法由反射系统在运行时为每个类型自动生成。
  • reflect.Type接口中的方法不是对所有类型都能使用,每个方法都有其特定的适用范围和前提条件,如果在不满足调用条件的情况下调用了某个方法,则会触发panic异常。比如Elem方法只适用于数组、channel、map、指针和切片类型,NumField和Field方法只适用于结构体类型,NumIn、In、NumOut和Out方法只适用于函数或方法类型。

字段信息

通过reflect.Type接口的Field或FieldByName方法,能够获取结构体中某个字段的字段信息,获取到的字段信息通过StructField结构体进行描述。StructField结构体的定义如下:

type StructField struct {
   
	Name string    // field name
	PkgPath string // package path

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}

字段说明:

  • Name:表示该字段的名称。
  • PkgPath:表示该字段所在的包路径,对于可导出的字段,PkgPath为空字符串。
  • Type:表示该字段对应的reflect.Type。
  • Tag:表示该字段的Tag标签信息。
  • Offset:表示该字段在结构体中的偏移量。
  • Index:表示该字段的索引序列。
  • Anonymous:表示该字段是否为匿名字段。

方法信息

通过reflect.Type接口的Method或MethodByName方法,能够获取对应类型所绑定的某个方法的方法信息,获取到的方法信息通过Method结构体进行描述。Method结构体的定义如下:

type Method struct {
   
	Name string    // method name
	PkgPath string // package path

	Type  Type  // method type
	Func  Value // func with receiver as first argument
	Index int   // index for Type.Method
}

字段说明:

  • Name:表示该方法的名称。
  • PkgPath:表示该方法所在的包路径,对于可导出的方法,PkgPath为空字符串。
  • Type:表示该方法对应的reflect.Type。
  • Func:表示该方法对应的reflect.Value。
  • Index:表示该方法在对应类型的方法集中的索引。

reflect.Value

reflect.Value

  • reflect.Value是reflect包中的一个类型,用于表示任意变量的值。
  • 通过reflect包中的ValueOf函数,可以获取持有指定变量的Value。
  • 通过reflect包中的New函数,可以创建指定类型的变量,并获取持有指向该变量的指针的Value。

reflect.ValueOf函数的函数原型如下:

func ValueOf(i interface{
   }) Value
func New(typ Type) Value

reflect.Value类型常用的方法如下:

方法名 功能
Kind 获取所持有的值对应的Kind
Type 获取所持有的值对应的Type
Elem 获取所持有的接口保管的值的Value封装,或获取所持有的指针指向的值的Value封装
Index 获取所持有的值的第i个元素的Value封装
NumField 获取所持有的结构体类型值的字段数
NumMethod 获取所持有的值所绑定的方法数
Field 获取所持有的结构体类型值的第i个字段的Value封装
Method 获取所持有的值所绑定的第i个方法的函数形式的Value封装
FieldByName 获取所持有的结构体类型值的字段中,指定字段名的字段的Value封装
MethodByName 获取所持有的值所绑定的方法中,指定方法名的方法的函数形式的Value封装
Call 指定参数调用所持有的函数,返回函数返回值的Value封装
Interface 返回所持有的值的interface{}类型值
Int、Float、Bool、String、Pointer 返回所持有的值的对应类型值,如果所持有的值不是对应的类型,则会触发panic异常
SetInt、SetFloat、SetBool、SetString、SetPointer 设置所持有的值,如果所持有的值不是对应的类型,则会触发panic异常
Set 将所持有的值设置为指定Value所持有的值,指定Value所持有值的类型必须与当前所持有值的类型相同,否则会触发panic异常

说明一下:

  • reflect.Value类型的方法不是对所有类型都能使用,每个方法都有其特定的适用范围和前提条件,如果在不满足调用条件的情况下调用了某个方法,则会触发panic异常。比如Elem方法只适用于接口和指针类型,NumField和Field方法只适用于结构体类型,Index方法只适用于数组、channel、切片和字符串类型,Call方法只适用于函数或方法类型。
  • 通过Method或MethodByName方法,获取v所持有的值所绑定的某个方法的函数形式的Value封装时,返回值持有的函数总是使用v所持有的值作为receiver(即第一个参数),因此返回值在调用Call方法时不用手动传入receiver参数。

reflect.Value与reflect.Type

reflect.Value是一个具体的类型,而reflect.Type被设计成了一个接口类型。其原因如下:

  • 类型的信息需要反射系统在运行时为每个类型自动生成,以适应各种未知的类型,将reflect.Type定义成接口类型的目的就是,指明运行时需要为每个类型生成哪些方法。
  • reflect.Value提供了各种访问和操作所持有值的方法,在使用reflect.Value时,通常已经知道所持有值的具体类型,这时通过reflect.Type即可获取到所持有值的类型信息,运行时不需要为其动态的生成任何方法,因此最终将reflect.Value定义成具体类型。

reflect.Kind

reflect.Kind

  • reflect.Kind是reflect包中的一个类型,用于表示类型的类别。
  • reflect.Type和reflect.Value都提供了对应的Kind方法,用于获取类型的Kind。

reflect.Kind本质是一个常量枚举类型。其定义如下:

type Kind uint

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Uintptr
	Float32
	Float64
	Complex64
	Complex128
	Array
	Chan
	Func
	Interface
	Map
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)

说明一下:

  • 类型和类别可能是一对一的,比如int类型对应的Kind是Int,float32类型对应的Kind是Float32。类型和类别也可能是一对多的,比如所有结构体类型对应的Kind都是Struct,所有指针类型对应的Kind都是Pointer。
  • 在获取变量的reflect.Value时,ValueOf函数或提取出接口值中对应的动态类型和动态值,并返回具体类型的Value。如果需要创建一个Kind为Interface的Value,可以先通过ValueOf函数获取一个指向接口的指针的Value,然后通过Value的Elem方法获取Value持有的指针指向的值的Value封装,这时获取到的Value的Kind就是Interface。

具体类型、空接口与reflect.Value的相互转换

具体类型、空接口与reflect.Value的相互转换

在反射过程中,变量的类型经常需要在具体类型、空接口类型和reflect.Value类型之间进行转换。其转换的方式如下:

  • 将变量由具体类型转换为空接口类型时,直接通过变量赋值的方式即可。
  • 将变量由空接口类型转换为reflect.Value类型时,通过调用reflect.ValueOf函数即可。
  • 将变量由reflect.Value类型转换为空接口类型时,通过调用reflect.Value的Interface方法即可。
  • 将变量由空接口类型转为具体类型时,需要借助类型断言。

转换示意图如下:

在这里插入图片描述

转换案例如下:

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
   
	Name 
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

2021dragon

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

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

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

打赏作者

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

抵扣说明:

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

余额充值