Golang标准库 ---初识反射---Reflect包

以下是反射的简短介绍

        

反射是指程序能够在运行时访问、检测和修改其自身状态或行为的一种能力。它允许程序动态地获取类型信息、调用方法、访问属性,甚至生成新的类型。

常用场景:

  • 反射常用于框架开发、插件系统、测试工具等需要高度动态性和灵活性的场景。

注意: 反射是一把双刃剑,使用时需权衡利弊。

reflect核心时TypeOf 和 ValueOf

TypeOf(i interface{}) Type

重点看这个返回值,它是一个接口,

type Type interface {
    // 变量的内存对齐,返回 rtype.align
    Align() int

    // struct 字段的内存对齐,返回 rtype.fieldAlign
    FieldAlign() int

    // 根据传入的 i,返回方法实例,表示类型的第 i 个方法
    Method(int) Method

    // 根据名字返回方法实例,这个比较常用
    MethodByName(string) (Method, bool)

    // 返回类型方法集中可导出的方法的数量
    NumMethod() int

    // 只返回类型名,不含包名
    Name() string

    // 返回导入路径,即 import 路径
    PkgPath() string

    // 返回 rtype.size 即类型大小,单位是字节数
    Size() uintptr

    // 返回类型名字,实际就是 PkgPath() + Name()
    String() string

    // 返回 rtype.kind,描述一种基础类型
    Kind() Kind

    // 检查当前类型有没有实现接口 u
    Implements(u Type) bool

    // 检查当前类型能不能赋值给接口 u
    AssignableTo(u Type) bool

    // 检查当前类型能不能转换成接口 u 类型
    ConvertibleTo(u Type) bool

    // 检查当前类型能不能做比较运算,其实就是看这个类型底层有没有绑定 typeAlg 的 equal 方法。
    // 打住!不要去搜 typeAlg 是什么,不然你会陷进去的!先把本文看完。
    Comparable() bool

    // 返回类型的位大小,但不是所有类型都能调这个方法,不能调的会 panic
    Bits() int

    // 返回 channel 类型的方向,如果不是 channel,会 panic
    ChanDir() ChanDir

    // 返回函数类型的最后一个参数是不是可变数量的,"..." 就这样的,同样,如果不是函数类型,会 panic
    IsVariadic() bool

    // 返回所包含元素的类型,只有 Array, Chan, Map, Ptr, Slice 这些才能调,其他类型会 panic。
    // 这不是废话吗。。其他类型也没有包含元素一说。
    Elem() Type

    // 返回 struct 类型的第 i 个字段,不是 struct 会 panic,i 越界也会 panic
    Field(i int) StructField

    // 跟上边一样,不过是嵌套调用的,比如 [1, 2] 就是说返回当前 struct 的第1个struct 的第2个字段,适用于 struct 本身嵌套的类型
    FieldByIndex(index []int) StructField

    // 按名字找 struct 字段,第二个返回值 ok 表示有没有
    FieldByName(name string) (StructField, bool)

    // 按函数名找 struct 字段,因为 struct 里也可能有类型是 func 的嘛
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    
    // 返回函数第 i 个参数的类型,不是 func 会 panic
    In(i int) Type

    // 返回 map 的 key 的类型,不是 map 会 panic
    Key() Type

    // 返回 array 的长度,不是 array 会 panic
    Len() int

    // 返回 struct 字段数量,不是 struct 会 panic
    NumField() int

    // 返回函数的参数数量,不是 func 会 panic
    NumIn() int

    // 返回函数的返回值数量,不是 func 会 panic
    NumOut() int

    // 返回函数第 i 个返回值的类型,不是 func 会 panic
    Out(i int) Type
}

 ValueOf(i interface{}) Value

type Value struct {
    // 反射出来此值的类型,rtype 是啥往上看,但可别弄错了,这 typ 是未导出的,从外部调不到 Type 接口的方法
    typ *rtype

    // 数据形式的指针值
    ptr unsafe.Pointer

    // 保存元数据
    flag
}

Value的方法

// 前提 v 是一个 func,然后调用 v,并传入 in 参数,第一个参数是 in[0],第二个是 in[1],以此类推
func (v Value) Call(in []Value) []Value

// 返回 v 的接口值或者指针
func (v Value) Elem() Value

// 前提 v 是一个 struct,返回第 i 个字段,这个主要用于遍历
func (v Value) Field(i int) Value

// 前提 v 是一个 struct,根据字段名直接定位返回
func (v Value) FieldByName(name string) Value

// 前提 v 是 Array, Slice, String 之一,返回第 i 个元素,主要也是用于遍历,注意不能越界
func (v Value) Index(i int) Value

// 判断 v 是不是 nil,只有 chan, func, interface, map, pointer, slice 可以用,其他类型会 panic
func (v Value) IsNil() bool

// 判断 v 是否合法,如果返回 false,那么除了 String() 以外的其他方法调用都会 panic,事前检查是必要的
func (v Value) IsValid() bool

// 前提 v 是个 map,返回对应 value
func (v Value) MapIndex(key Value)

// 前提 v 是个 map,返回所有 key 组成的一个 slice
func (v Value) MapKeys() []Value

// 前提 v 是个 struct,返回字段个数
func (v Value) NumField() int

// 赋值
func (v Value) Set(x Value)

// 类型
func (v Value) Type() Type

反射的应用场景

  • 动态创建对象: 无需硬编码类型,便可根据条件创建实例。

  • 动态调用方法: 绕过编译时类型检查,根据字符串名称调用方法。

  • 动态访问属性: 类似方法调用,可根据字符串名称访问和修改属性值。

比如:

        序列化和ORM框架就可以通过反射拿到Tag标签进行解析和数据库字段进行映射

        还有ORM框架自动建表也是用过反射实现的

通过反射拿到Tag

func TestReflect_tag(t *testing.T) {
	U := User{
		Name: "张三",
		Age: 22,
	}
	for i := 0; i < reflect.TypeOf(U).NumField(); i ++{
		if tag,ok := reflect.TypeOf(U).Field(i).Tag.Lookup("json"); ok {
			fmt.Println(tag)
		}
		fmt.Println(reflect.TypeOf(U).Field(i).Tag.Get("test"))
	}
}
type User struct {
	Name string `json:"name" test:"name"`
	Age  int    `json:"age" test:"age"`
}

动态调用函数

// 动态调用函数
func TestReflect_method(t *testing.T) {
	var r = ref{}
    //无参
	reflect.ValueOf(r).MethodByName("RefmethodV1").Call(nil)
	a := reflect.ValueOf(1)
	b := reflect.ValueOf("123")
	in := []reflect.Value{a, b}
    //有参
	reflect.ValueOf(r).MethodByName("RefmethodV2").Call(in)
}

type ref struct{}

func (t ref) RefmethodV1() {
	fmt.Println("动态调用函数")
}
func (t ref) RefmethodV2(a int, b string) {
	fmt.Println("hello"+b, a)
}

 欢迎加入我们的golang&DTcloud&erp方向的交流群:   576401014

  • 经验分享,问题解答

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DTCloud4

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

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

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

打赏作者

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

抵扣说明:

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

余额充值