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

网友投稿 1073 2022-09-30

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

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

目录1. 反射的http://引入与介绍2. 反射的数据结构3. 如何通过反射对象来修改原数据对象的值?

1. 反射的引入与介绍

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

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

有时你需要编写一个函数,但是并不知道传给你的参数类型是什么,可能是没约定好;也可能是传入的类型很多,这些类型并不能统一表示。这时反射就会用的上了。有时候需要根据某些条件决定调用哪个函数,比如根据用户的输入来决定。这时就需要对函数和函数的参数进行反射,在运行期间动态地执行函数。

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

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

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

2. 反射的数据结构

type Type interface {

// 所有的类型都可以调用下面这些函数

// 此类型的变量对齐后所占用的字节数

Align() int

// 如果是 struct 的字段,对齐后占用的字节数

FieldAlign() int

// 返回类型方法集里的第 `i` (传入的参数)个方法

Method(int) Method

// 通过名称获取方法

MethodByName(string) (Method, bool)

// 获取类型方法集里导出的方法个数

NumMethod() int

// 类型名称

kgHNl 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,kgHNl 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的“类型”和“数据值”是在“一起的”,而反射的“类型”和“数据值”是分开的。

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

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

reflect.TypeOf(x)reflect.ValueOf(x)

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

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:802.11无线网络—802.11无线网络权威指南读书笔记
下一篇:计算机存储—大话存储II读书
相关文章

 发表评论

暂时没有评论,来抢沙发吧~