深度解密Go语言之反射

2020-07-09 00:00:00 函数 字段 变量 类型 反射
更好的阅读效果:cnblogs.com/qcrao-2018/

反射和 Interface 息息相关,而 Interface 是我们上一篇文章的内容。在开始正文前,和大家说点题外话。

上一篇关于 Interface 的文章发出后,获得了很多的关注和阅读。比如,登上了 GoCN 的每日新闻条:



可能是编辑者觉得这篇文章称不上“深度解密”,把标题给小小地改动了下,哈哈~~

在博客园登上了 48 小时阅读排行榜:



在开发者头条 APP (类似于今日头条,不过内容都是技术相关的,还挺有意思的)上收获了 150 收藏,并被推荐到首页显眼的 banner 位置,阅读量达到了 1w 多,只是不知道这个数字是否是真实的,有点难以相信。



很多同学在后台向我反映文章太长了,不利于阅读,建议拆分一下。我非常理解,读屏时代,大家需要快速地读完全文,拿到收益。而码农桃花源的文章都非常长,读者很难在短时间内读完,并且获得相应的收益。

首先非常感谢大家的建议!其实我的想法是这样的:大家都在说,现在是一个信息严重过载的时代,信息多得看不完,不免产生很多焦虑。但是,我想说,优质的信息真有那么多吗?在我看来,文章的水平都是参差不齐,很多毫无内容和价值,大家把时间浪费在这些信息上面是很不值得的。因为你读了这些文章,就没有了读其他好的文章的精力。

所以,码农桃花源想做一个优质信息源,提供优质的内容。每一篇文章都是有深度,有内容,有收获。一篇文章,我一般得花费 2 周左右,算是半月更,和那些日更的没法比。当然,只是在数量上没法比。而这个时代,不缺的就是数量。

另外,文章长也算是我的一个特色。我完全可以拆分成上、中、下等等,但我希望一次性交付给我的读者所有有价值的内容。这样,你可以集中一个小时或是更长时间,精读完一篇文章。

微信公众号阅读文章有时不是太方便,这里推荐大家用微信读书,直接搜索公众号名字就可以看到公众号所有的文章了,很方便。这个功能出来很久了,我也是不久前才知道,推荐给大家。

闲话结束,今天要讲的内容是反射,进入正题。

什么是反射

直接看维基百科上的定义:

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

那我就要问个问题了:不用反射就不能在运行时访问、检测和修改它本身的状态和行为吗?

问题的回答,其实要首先理解什么叫访问、检测和修改它本身状态或行为,它的本质是什么?

实际上,它的本质是程序在运行期探知对象的类型信息和内存结构,不用反射能行吗?可以的!使用汇编语言,直接和内层打交道,什么信息不能获取?但是,当编程迁移到语言上来之后,就不行了!就只能通过反射来达到此项技能。

不同语言的反射模型不尽相同,有些语言还不支持反射。《Go 语言圣经》中是这样定义反射的:

Go 语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。

为什么要用反射

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

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

在讲反射的原理以及如何用之前,还是说几点不使用反射的理由:

  1. 与反射相关的代码,经常是难以阅读的。在软件工程中,代码可读性也是一个非常重要的指标。
  2. Go 语言作为一门静态语言,编码过程中,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接 panic,可能会造成严重的后果。
  3. 反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关键位置的代码,尽量避免使用反射特性。

反射是如何实现的

上一篇文章讲到了 interface,它是 Go 语言实现抽象的一个非常强大的工具。当向接口变量赋予一个实体类型的时候,接口会存储实体的类型信息,反射就是通过接口的类型信息实现的,反射建立在类型的基础上。

Go 语言在 reflect 包里定义了各种类型,实现了反射的各种函数,通过它们可以在运行时检测类型的信息、改变类型的值。

* 和 interface

Go 语言中,每个变量都有一个静态类型,在编译阶段就确定了的,比如 int, float64, []int 等等。注意,这个类型是声明时候的类型,不是底层数据类型。

Go 官方博客里就举了一个例子:

type MyInt int

var i int
var j MyInt

相关文章