文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Go反射底层原理及数据结构解析

2024-04-02 19:55

关注

1. 反射的引入与介绍

在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

需要反射的 2 个常见场景:

Java中动态代理与AOP的实现,就要借助这种操作。而对于我们Go语言来说,也提供了反射的机制。

有了前面对于interface{}底层数据结构的了解,Go中的每个实例对象可以分为两快,类型信息与数值信息。而我们Go中提供的反射机制,能分别拿到这两块信息。

image.png

数据interface中保存有结构数据,拿到该数据对应的内存地址,然后把该数据转成interface,通过查看interface中的类型结构,就可以知道该数据的结构了,其实以上就是Go反射通俗的原理。

image.png

2. 反射的数据结构

type Type interface {
    // 所有的类型都可以调用下面这些函数
	// 此类型的变量对齐后所占用的字节数
	Align() int
	// 如果是 struct 的字段,对齐后占用的字节数
	FieldAlign() int
	// 返回类型方法集里的第 `i` (传入的参数)个方法
	Method(int) Method
	// 通过名称获取方法
	MethodByName(string) (Method, bool)
	// 获取类型方法集里导出的方法个数
	NumMethod() int
	// 类型名称
	Name() string
	// 返回类型所在的路径,如:encoding/base64
	PkgPath() string
	// 返回类型的大小,和 unsafe.Sizeof 功能类似
	Size() uintptr
	// 返回类型的字符串表示形式
	String() string
	// 返回类型的类型值
	Kind() Kind
	// 类型是否实现了接口 u
	Implements(u Type) bool
	// 是否可以赋值给 u
	AssignableTo(u Type) bool
	// 是否可以类型转换成 u
	ConvertibleTo(u Type) bool
	// 类型是否可以比较
	Comparable() bool
	// 下面这些函数只有特定类型可以调用
	// 如:Key, Elem 两个方法就只能是 Map 类型才能调用
	// 类型所占据的位数
	Bits() int
	// 返回通道的方向,只能是 chan 类型调用
	ChanDir() ChanDir
	// 返回类型是否是可变参数,只能是 func 类型调用
	// 比如 t 是类型 func(x int, y ... float64)
	// 那么 t.IsVariadic() == true
	IsVariadic() bool
	// 返回内部子元素类型,只能由类型 Array, Chan, Map, Ptr, or Slice 调用
	Elem() Type
	// 返回结构体类型的第 i 个字段,只能是结构体类型调用
	// 如果 i 超过了总字段数,就会 panic
	Field(i int) StructField
	// 返回嵌套的结构体的字段
	FieldByIndex(index []int) StructField
	// 通过字段名称获取字段
	FieldByName(name string) (StructField, bool)
	// FieldByNameFunc returns the struct field with a name
	// 返回名称符合 func 函数的字段
	FieldByNameFunc(match func(string) bool) (StructField, bool)
	// 获取函数类型的第 i 个参数的类型
	In(i int) Type
	// 返回 map 的 key 类型,只能由类型 map 调用
	Key() Type
	// 返回 Array 的长度,只能由类型 Array 调用
	Len() int
	// 返回类型字段的数量,只能由类型 Struct 调用
	NumField() int
	// 返回函数类型的输入参数个数
	NumIn() int
	// 返回函数类型的返回值个数
	NumOut() int
	// 返回函数类型的第 i 个值的类型
	Out(i int) Type
        // 返回类型结构体的相同部分
	common() *rtype
	// 返回类型结构体的不同部分
	uncommon() *uncommonType
}
type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}

反射的实现和interface的组成很相似,都是由“类型”和“数据值”构成,但是值得注意的是:interface的“类型”和“数据值”是在“一起的”,而反射的“类型”和“数据值”是分开的。

TypeValue提供了非常多的方法:例如获取对象的属性列表、获取和修改某个属性的值、对象所属结构体的名字、对象的底层类型(underlying type)等等

Go中的反射,在使用中最核心的就两个函数:

这两个函数可以分别将给定的数据对象转化为以上的TypeValue。这两个都叫做反射对象

3. 如何通过反射对象来修改原数据对象的值?

在Go中,任何函数的参数都是值的拷贝,而非原数据。

反射函数reflect.ValueOf()也不例外。我们目前得到的反射对象,都是原对象的copy的反射对象,而非原对象本身,所以不可以修改到原对象.

那如何修改呢?

首先,在Go中要想让函数“有副作用“,传值必须传指针类型的。

var x float64 = 5.7
v := reflect.ValueOf(&x)

时还不行,因为这样反射对象对应的是原数据对象的指针类型,必须要拿到当前类型的值类型(*v)。 Go提供了另外一个方法Elem()

p := v.Elem()
fmt.Println(p.CanSet()) // true

p.SetFloat(6.6)
fmt.Println(x) // 6.6

经过以上操作,就可以修改原数据了,完整过程如下:

var x float64 = 5.7
v := reflect.ValueOf(&x)
p := v.Elem()
fmt.Println(p.CanSet()) // true
p.SetFloat(6.6)
fmt.Println(x) // 6.6

到此这篇关于Go反射底层原理及数据结构解析的文章就介绍到这了,更多相关 Go反射原理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯