什么是反射

维基百科上反射的定义:

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

我们知道一个变量在定义的时候是知道类型的,但是在运行中,可能会被随时转型,不管是显式还是隐式。

其本质就是程序在运行期探知对象的类型信息和内存结构。如果是汇编语言,我们完全没有这种烦恼。

其实什么是类型?只是确定如何解析这几段空间的0和1。

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

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

反射的使用场景

其实Go比较尴尬的点在于没有泛型。所以用了interface这种来定义“不定参数”。而反射大多就是用来解析interface变量的。

但是通常反射的代码往往是有坑的:

  • 不好阅读。
  • 一旦有没判断好的地方,就容易panic。
  • 无法在编译的时候发现问题。
  • 运行速度较慢。

在这里还是期待Go的泛型早日出来。

Go是怎么实现反射的

想想如果我们,要随时知道这个变量的类型,我们会怎么做?

记下来呗。

Go也是一样,一个记录了taye,一个记录了值,当然这个值是一个指针,在需要获取值的时候,根据type执行对应的函数。

对外暴露了两个方法:

func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.type)
}
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
// ……
return unpackEface(i)
}
// 分解 eface
func unpackEface(i interface{}) Value {
e := (*emptyInterface)(unsafe.Pointer(&i))
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) {
f |= flagIndir
}
return Value{t, e.word, f}
}

将先将 i 转换成 *emptyInterface 类型, 再将它的 typ 字段和 word 字段以及一个标志位字段组装成一个 Value 结构体,而这就是 ValueOf 函数的返回值,它包含类型结构体指针、真实数据的地址、标志位。

回到思路原点,其实就是返回了一个类型,一个地址,表达了如果去解析这段地址。

当然,这只是冰山一角,具体细节还望自行看源码。

反射的三大定律

  1. Reflection goes from interface value to reflection object.
  2. Reflection goes from reflection object to interface value.

前两条是说可以通过反射对象得到interface变量,也可以将interface变量转化为反射对象。

  1. To modify a reflection object, the value must be settable.

这是说如果你要改变一个反射对象,它的值必须是可修改的。

举个栗子:

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.

我们看valueOf的代码知道,它只是将传进去的变量的地址转为了emptyInterface,返回了一种解析方式,告诉你这个变量是什么类型,你应该如果解析它。

如果需要改变量,我们需要先的到这个变量的具体位置。

func (v Value) Elem() Value {
k := v.kind()
switch k {
case Interface:
var eface interface{}
if v.typ.NumMethod() == 0 {
eface = *(*interface{})(v.ptr)
} else {
eface = (interface{})(*(*interface {
M()
})(v.ptr))
}
x := unpackEface(eface)
if x.flag != 0 {
x.flag |= v.flag.ro()
}
return x
case Ptr:
ptr := v.ptr
if v.flag&flagIndir != 0 {
ptr = *(*unsafe.Pointer)(ptr)
}
// The returned value's address is v's value.
if ptr == nil {
return Value{}
}
tt := (*ptrType)(unsafe.Pointer(v.typ))
typ := tt.elem
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind())
return Value{typ, ptr, fl}
}
panic(&ValueError{"reflect.Value.Elem", v.kind()})
}

这里看到,要么interface,要么指针,其他的都会panic。

所以:

var x float64 = 3.14
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet()) v2 := reflect.ValueOf(&x)
p := v2.Elem()
fmt.Println("settability of p:", p.CanSet())

输出:

settability of v: false
settability of p: true

接语

看很多遍不如自己看一遍源码,其实很少的。

如果有不懂的还是欢迎评论,我都会回答的。

【Go语言细节】反射的更多相关文章

  1. C语言细节——献给入门者(三)

    C语言细节——献给入门者(三) >>主题:关于强制类型转换 先来瞎扯下强制类型转换,c语言有很多数据类型,long,short,int,float,double,bool,char等等.当 ...

  2. C语言细节——献给初学者(二)

    C语言细节——献给初学者(二) 主题  循环运用+选择判断 C语言循环有for和while/do...while: 选择判断有:if...else和switch...case 在循环中需要注意搭配br ...

  3. C语言细节——献给入门者(一)

    C语言细节——献给入门者(一) 主题  输入输出需要注意的细节 首先我们要知道大致有scanf(),printf(),getchar(),putchar(),gets(),puts()这几种输入方式. ...

  4. C语言细节总结笔记

    C语言细节总结笔记 */--> C语言细节总结笔记 Table of Contents 1. 三步异或法交换数字 2. 做差法交换数字 3. 按n位置位 4. 求余求商求积 5. 辗除法求最大公 ...

  5. go语言通过反射获取和设置结构体字段值的方法

    本文实例讲述了go语言通过反射获取和设置结构体字段值的方法.分享给大家供大家参考.具体实现方法如下: type MyStruct struct { N int } n := MyStruct{ 1 } ...

  6. Go语言之反射(一)

    反射 反射是指在程序运行期对程序本身进行访问和修改的能力.程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分.在运行程序时,程序无法获取自身的信息.支持反射的语言可以在程序编译期将 ...

  7. [转载] C语言细节,写的非常棒!

    这篇文章主要讨论C语言细节问题.在找一份工作的时候,语言细节占的比例非常小,之前看某个贴着讨论,估计语言细节在面试中,占了10%的比重都不到,那为什么还要研究C语言的细节呢,我觉得有三个原因促使我总结 ...

  8. 深度解密Go语言之反射

    目录 什么是反射 为什么要用反射 反射是如何实现的 types 和 interface 反射的基本函数 反射的三大定律 反射相关函数的使用 代码样例 未导出成员 反射的实际应用 json 序列化 De ...

  9. Go语言 8 反射

    文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/ Go学习群:415660935 8.1概念和作用 Reflection(反射)在计算 ...

随机推荐

  1. webpack learn1-配置项目加载各种静态资源及css预处理器2

    继续在webpack.config.js中配置loader { test:/\.css$/, use: [ 'style-loader', 'css-loader' ] },{ test:/\.(jp ...

  2. Apache AB(1) - 快速使用

    前言 Apache AB的优缺点 十分快捷.简单 只适用HTTP协议 该工具更加适用于单接口性能压测 参数化实现麻烦:被测接口没有太多参数化 不支持场景化,不能将上下文串联起来 适用于快速开发小的场景 ...

  3. mysql 不常用的存储引擎

    csv 数据文件可以编辑;每一列不能为空,不支持索引:文件保存数据,cat可以查看数据;用处:数据交换中间表--excel表导入数据等; Archive 对表数据进行压缩,磁盘i/o减少:节省空间;只 ...

  4. $.ajax 常用的套路

    $.ajax 常用的套路 (function(){ window.webApi = new Object(); webApi.get = function(url,data,callback){ $. ...

  5. python的列表和java的数组有何异同

    今天面试被问到,自己学习一下. python的列表是可变长的,定义时不需要指定长度:pyhton是弱对象类型,python的列表存储的数据类型可以不相同:python的列表更加灵活,如可以通过''命令 ...

  6. 配置php redis 扩展

    参照runoob:PHP 使用 Redis Windows: - 假设redis已经安装好 服务启动 - xampp (php 7.1 x86 windows) 查看phpinfo (php 7.1 ...

  7. 『GoLang』面向对象

    我们总结一下前面看到的:Go 没有类,而是松耦合的类型.方法对接口的实现. 面向对象语言最重要的三个方面分别是:封装,继承和多态,在 Go 中它们是怎样表现的呢? Go实现面向对象的两个关键是stru ...

  8. P5074-Eat the Trees【插头dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P5074 题目大意 给出一个\(n\times m\)的网格,有的必须铺线有的不能,铺成若干条闭合回路,求方案数. ...

  9. 【月光宝盒get√】用时间置换空间,聊聊稀疏数组的那些事儿

    背景 数据结构是指带有结构特性的数据元素的集合.在数据结构中,数据之间通过一定的组织结构关联在一起,便于计算机存储和使用.从大类划分,数据结构可以分为线性结构和非线性结构,适用于不同的应用场景. 线性 ...

  10. python基础知识三——try与except处理异常语句

    try/except介绍 与其他语言相同,在python中,try/except语句主要是用于处理程序正常执行过程中出现的一些异常情况,如语法错(python作为脚本语言没有编译的环节,在执行过程中对 ...