Golang反射原理详解

Golang反射原理详解

反射的概念

适用场景

反射的优缺点

反射的优点

反射的缺点

Golang的反射

Golang反射的基本原理

Golang反射提供的能力

Go反射三定律

Golang中反射的实际运用

结尾

Golang反射原理详解

反射是计算机语言提供的一个关键特性,掌握它,对我们编写通用(不要写死)的代码有比较大的帮助,另外,一些库或者框架提供的关键特性也是通用反射来实现,掌握反射,可以使我们更好理解这些功能的实现.

本文试图通过反射的概念,适用场景,Golang中的反射,Golang的实际运用 四个方面来搞懂反射.

全文看完大概需要15分钟左右,如时间不充裕,建议收藏后仔细阅读.

反射的概念

反射本来是一个物理学中的名词,比如光的反射,声音的反射等.

这里说的是反射是指反射编程, 本文中要讨论的反射都是指反射编程. 根据维基百科的定义,反射编程是指在程序运行期间,可以访问,检测和修改它本身状态或者行为的一种能力.用比喻来说,反射就是程序运行的时候能够"观察"并且修改自己的行为的一种能力.

适用场景

不能预先知道函数参数类型,或者参数类型有很多种,无法用同一个类型来表示

函数需要根据入参来动态的执行不同的行为

反射的优缺点

反射的优点

可以在一定程序上避免硬编码,提供灵活性和通用性

可以作为一个第一个类对象发现并修改源代码的结构(如代码块,类,方法,协议等)

反射的缺点

由于将部分类型检查工作从编译期推迟到了运行时,使得一些隐藏的问题无法通过编译期发现,提高了代码出现bug的几率,搞不好就会panic

反射出变量的类型需要额外的开销,降低了代码的运行效率

反射的概念和语法比较抽象,过多的使用反射,使得代码难以被其他人读懂,不利于合作与交流

Golang的反射

Golang反射的基本原理

Golang是怎么实现在程序运行的时候能够"观察"并且修改自己的行为的能力的呢

Golang反射是通过接口来实现的,通过隐式转换,普通的类型被转换成interface类型,这个过程涉及到类型转换的过程,首先从Golang类型转为interface类型, 再从interface类型转换成反射类型, 再从反射类型得到想的类型和值的信息.

总的基本流程见下面的图.


在Golang obj转成interface这个过程中, 分2种类型

包含方法的interface, 由runtime.iface实现

不包含方法的interface, 由runtime.eface实现

这2个类型都是包含2个指针, 一个是类型指针, 一个是数据指针, 这2个指针是完成反射的基础.

实质上, 通过上述转换后得到的2种interface, 已经可以实现反射的能力了. 但作为语言本身, 标准库将这个工作封装好了, 就是 reflect.Type与reflect.Value , 方便我们使用反射.

reflect. TypeOf 和 reflect.ValueOf 是一个转换器, 完成反射的的最终转换, 得到 reflect.Type, reflect.Value 对象, 得到这2个对象后, 就可以完成反射的准备工作了, 通过 reflect.Type, reflect.Value 这对类型, 可以实现反射的能力.

上图中, 最后一根线说的是由reflect. Value变成普通inteface的过程, 然后通过具体的类型断言, 转成真正的类型. 可能有人会觉得奇怪, 为什么 reflect.Value 可以转成interface对象, reflect. Type 不行呢, 这个留给读这个文章的你去思考, 相信会有答案.

Golang反射提供的能力

运行时获取对象的类型, 值

创建对象, 执行方法

反射对象转换成Go语言对象

动态修改对象的值

已经有人将这些能力总结成反射三定律

Go反射三定律

如同物理反射定律一样, 反射编程中也有反射定律. 这个反射定律是在go语言官方博客中, The Laws of Reflection 有兴趣的可以点开一看. 本文简单概括一下就是下面三点.

Golang对象可以转换成反射对象

反射对象可以转换成Golang对象

可寻址的reflect对象可以更新值

Golang中反射的实际运用

反射在标准库中和第三方库中有着大量的运用, 这里举几个子来说明反射的运用

encoding/json marshal方法

fmt. Printf

各种orm工具

下面仅列举json序列化的例子

func(e*encodeState)marshal(vinterface{},opts encOpts)(errerror){deferfunc(){ifr:=recover();r!=nil{ifje,ok:=r.(jsonError);ok{err=je.error}else{panic(r)}}}()e.reflectValue(reflect.ValueOf(v),opts)returnnil}

这里将interface v通过 reflect.ValueOf 转换成 reflect.Value 对象进一步做下处理.

func(e*encodeState)reflectValue(v reflect.Value,opts encOpts){valueEncoder(v)(e,v,opts)}funcvalueEncoder(v reflect.Value)encoderFunc{if!v.IsValid(){returninvalidValueEncoder}returntypeEncoder(v.Type())}funcnewTypeEncoder(t reflect.Type,allowAddrbool)encoderFunc{// If we have a non-pointer value whose type implements// Marshaler with a value receiver, then we're better off taking// the address of the value - otherwise we end up with an// allocation as we cast the value to an interface.ift.Kind()!=reflect.Ptr&&allowAddr&&reflect.PtrTo(t).Implements(marshalerType){returnnewCondAddrEncoder(addrMarshalerEncoder,newTypeEncoder(t,false))}ift.Implements(marshalerType){returnmarshalerEncoder}ift.Kind()!=reflect.Ptr&&allowAddr&&reflect.PtrTo(t).Implements(textMarshalerType){returnnewCondAddrEncoder(addrTextMarshalerEncoder,newTypeEncoder(t,false))}ift.Implements(textMarshalerType){returntextMarshalerEncoder}switcht.Kind(){casereflect.Bool:returnboolEncodercasereflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64:returnintEncodercasereflect.Uint,reflect.Uint8,reflect.Uint16,reflect.Uint32,reflect.Uint64,reflect.Uintptr:returnuintEncodercasereflect.Float32:returnfloat32Encodercasereflect.Float64:returnfloat64Encodercasereflect.String:returnstringEncodercasereflect.Interface:returninterfaceEncodercasereflect.Struct:returnnewStructEncoder(t)casereflect.Map:returnnewMapEncoder(t)casereflect.Slice:returnnewSliceEncoder(t)casereflect.Array:returnnewArrayEncoder(t)casereflect.Ptr:returnnewPtrEncoder(t)default:returnunsupportedTypeEncoder}}

这里通过 reflect.Type 类型, 来初始化不同的encoder, 大量运用了反射, 实现了序列化, 在不支持反射的语言如c++, 实现对象json序列化, 就比较麻烦.

结尾

Golang反射严重依赖于 interface{} 这个万能的 容器 类型, 这个 interface{} 类型相当于java中的class类型, 是实现反射的桥梁. 我们在谈Golang反射时, 主要还是围绕 interface{} 展开来说的.

反射是现代静态语言的通用底层技术, 能在一定程序上提升静态类型的灵活性.

全文完, 如果你觉得有用, 欢迎点赞, 收藏, 关注 三连, 谢谢阅读. 如有你对本文有要需要讨论的点, 欢迎留言讨论.

你可能感兴趣的:(Golang反射原理详解)