GOLANG 反射法则
译自[blog.golang.org/laws-of-reflection]
在计算机中, 反射是程序通过类型,检测到它自己的结构能力;是一种元编程程;也是一个具大的混淆点
在本文中,我们将通过解释反射是如何在GO中工作的来澄清它。每个语言的反射模式是不同的。本文着重于GO,
所以后文中的反射都是指GO中的反射
1.类型和接口
因为反射是修建于类型系统之上, 所以让我们从GO的类型开始讲吧。
GO是静态类型语言。 每个变量都有一个静态类型。 也就是说, 每一个已经类型在编译时已经固定了其类型:int, float32...等
假如我们声明如下:
type MyInt int
var i int
var j MyInt
则i的类型为int; j的类型为MyInt;变量i和j有明显不同的静态类型, 而潜在下层类型, 他们可以彼此赋值不须要转换。
还有一种重要类型是接口类型,它代表了一组固定的方法集。一个接口变量能存储任一实体值,只要它实现了接口方法集。以大家所
熟知的接口为例, io.Reader和io.Writer, 它们的类型摘自io 包。
// Reader 就是一个包含了基本读方法的接口
type Reader interface {
Read(p []byte[(n int, err error)
}
// Writher就是一个包含了基本写方法的接口
type Writer interface{
Write(p []byte)(n int, err error);
}
任何实现了上述读(写)方法签名的类型就说它实现了io.Reader(或者io.Writer).讨论这个的目标是, 就是指一个io.Reader类型的变量
可以带有任何值,只要它的类型中有一个Read方法
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new (bytes.Buffer)
// 等等 ....都可以的...
无论r带有什么实例值,r的类型总是io.Reader,明白这点是非常重要的;GO是静态类型,r的静态类型就是io.Reader。
另一个极其重要的接口例子就是空接口
interface{}
它代表一个空方法集。它可以被任何值满足。因为任何值都有0到多个方法
有些朋友说,GO的接口是动态类型的, 这就是错误引导。 他们是确实静态类型:一个接口类型的变量总是有一个相同的静态类型,
虽然,在运行时,一个存储在一个接口变量上的值可能会改变类型,但值总是满足接口。
我们需要准确的了解这些,因为反射和接口太相似了。
2. 接口的表达
Russ Cox写了一篇博客关于GO语言接口值的表达【research.swtch.com/interface】, 我就没有必须重复这个故事了。简单总结如下
接口类型的变量存成一个pair(对):值赋给变量和它的描述符descriptor;更准确的说,就是潜在下层的实例值实现了接口,它的类型
描述了下层实例值的完整类型。例如:
var r io.Reader
tty , err := os.OpenFile("/dev/tty",os.O_RDWR, 0)
if er != nil{
return nil, err
}
r = tty
r包含了(值value,类型type)pair对,(tty,*os.File). 注意,*os.File类型实现了远不只Read方法;虽然接口值提供只有Read方法。
内部值包含所有关于值的类型信息,这就是为什么我们可以如下处理
var w io.Writer
w = r.(io.Writer) // 类型断言转换
这在个赋值表达式中是一个类型断言;它判言的是r里面的东西也实现了io.writer.并且我们能赋值给w, 赋值后,w包含(tty,*os.File)对。
和之前r所包含的对是一样的的。接口的静态类型确定了通过接口变量那些方法可以被调用。虽然内部实例值可能有一个更大的方法集。
接着,我们也可以
var empty interface{}
empty = w
我们的空接口值empty,再一次包含了一样的(tty,*os.File)对。这是很方便的, 空接口可以含有什么信息
一个重要的细节就是, 内部pair对总是有一个(值value,实类型concrete type)并且没有(value, 接口类型interface type)
现在我们可以谈反射了
反射法则
1、反射-从接口值到反射对象
从基本上讲, 反射仅是一种用来检测存储在接口变量内部(值value,实例类型concret type)pair对的一种机
制。 开始,在反射包中,有两种类型我们需要了解类型和值。 这两种类型让我们可以访问接口变量内容。
两个简单函数分别为reflect.TypeOf和reflect.Valueo,它们从接口变量中取出reflect.Type和
reflect.Value,(也可以从reflect.Value轻松获得reflect.Type, 但是现在我们还是将Value和Type概念分
开来将)
让我们首先来讲TypeOf
package main
import(
"fmt"
"reflect"
)
func main(){
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x);
}
程序输出
type:float64
你可能会想这里的接口变量在哪里呀,因为程序看来像是传递的一个float64变量x, 不是接口值, 对于
reflect.TypeOf,包含了一空接口:
func TypeOf(i inteface{}) Type
当我们调用reflect.TypeOf(x),首先x被存在一个空接口中,它被当前该接口变量传入到reflect.TypeOf中;
reflect.TypeOf解包将空接口转为类型信息
reflect.ValueOf函数,找回值(从这里,我们省略代码模板,聚焦到执行代码上)
var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x))
输出
value: <float64 Value>
reflect.Type和reflect.Value都有方法配槽来让我们来检查与操作它们。 一个重要的例子如,Value有一个
Type方法返回reflect.Value的类型Type。另一个就是Type和Value都有一个Kind方法返回一个常量表明它是
哪种存储类型Uint,Float64,Slice等等。 此外,在Value上的有方法名如Int和Float让我们取出里面的值
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:",v.Kind() == reflect.Float64)
fmt.Println("value:",v.Float)
输出:
type:float64
kind is float64:true
value: 3.4
还有方法如SetInt和SetFloat,但是使用它们要理解他们的设置能力,反射的第三条法则,讨论如下:
反射库有一些属性值得列出来,首先,保持API简单, 值的getter和setter操作在它可以操作的最大的类型
上:对于所有有符号数为Int64; 例如,值的Int方法返回一个int64,SetInt需要一个int64; 它可能需要转换
成实际值
var x uint8 = 'x'
v := reflect.ValueOf(x)
fmt.Println("type:",v.Type()) // uint8
fmt.Println("kind is uint8:", v.Kind() == reflect.Uint8) // true
x = uint8(v.Uint()) // v.Uint返回uint64
第二个属性就是,反射对象的Kind描述了潜在下层的类型Type, 并不是静态类型。 假如一个反射对象包含一个用户定义整形类型的值,如下:
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
v的Kind仍然是reflect.Int, 虽然x的静态类型是MyInt,而不是int, 换名话说,Kind不区分int和MyInt,虽然Type能。
反射第二法则
2.反射-从反射对象到接口值
就像物理反射一样,反射在GO生成自己的反像。
给定一个reflect.Value我们使用Interface方法能找回一个接口值;在effect方法打包了类型和值信息到一个接口中并返回结果。
// 返回v的值作为一个接口变量
func (v Value)Interface() interface{}
由此我们可以说
y := v.Inteface().(float64) // y 有类型float64
fmt.Println(y)
。。。。。。。待续
GOLANG 反射法则的更多相关文章
- golang反射初试
golang反射来自Go AST(Abstract Syntax Tree). reflect操作更多像traverse AST. t := reflect.TypeOf(obj) 使用TypeOf( ...
- golang 反射应用(二)
golang反射应用(二) package test import ( "reflect" "testing" ) //定义适配器 func TestRefle ...
- Golang反射机制
Go反射机制:在编译不知道类型的情况下,可更新变量.在运行时查看值.调用方法以及直接对它们的布局进行操作. 为什么使用反射 有时需要封装统一接口对不同类型数据做处理,而这些类型可能无法共享同一个接口, ...
- golang 反射
参考:|--http://blog.51cto.com/speakingbaicai/1707637 |--https://studygolang.com/articles/6324 反射是在gola ...
- [golang]反射的用处--代码自动生成
背景: go语言处理db.json的时候,具体代码的变量定义和db字段.json输出的时候可能不一样. 这个时候,我们需要用tag的方式来进行定义. 例如: type MyStruct struct ...
- golang 反射中调用方法
反射中调用函数 众所周知,golang中的函数是可以像普通的int.float等类型变量那样作为值的,例如: package main import "fmt" func hell ...
- GO开发[六]:golang反射(reflect)
反射 反射:可以在运行时动态获取变量的相关信息 Import ("reflect") reflect.TypeOf,获取变量的类型,返回reflect.Type类型 refle ...
- golang反射
要点 1.变量 2.反射 3.结构体反射 4.反射总结以及应用场景 一.变量介绍 1.变量的内在机制 A.类型信息,这部分是元信息,是预定义好的 B.值类型,这部分是程序运行过程中,动态改变的 var ...
- golang反射举例
反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:并且能改变它的属性. package main import ( "f ...
随机推荐
- 循环语句for
循环语句for 格式: for(int i=1/*初始条件*/;i<=100/*循环条件*/;i++/*状态改变*/) { //循环体,执行代码:(break:跳出循环体) } 给出初始条件,先 ...
- Go运行环境搭建(Mac\Linux)
转载:http://blog.csdn.net/nellson/article/details/51523159 1. 下载安装文件 http://www.golangtc.com/download ...
- BZOJ4033 [HAOI2015]T1
令$f[p][i]$表示以$p$为根的子树内,选了$i$个黑点,剩下的都是白点的这个子树内贡献的答案 如果$p$的子树都算出来了,只要计算$p$与$fa[p]$之间的边对答案的贡献就好了,贡献是$di ...
- dll和ocx比较
ActiveX,OLE是基于COM的一种应用,其文件后缀一般以dll和ocx结尾:ocx作为一种特殊的dll文件,具有一定的用户界面和事件响应,而dll文件只是方法和属性的集合. 一.关于DLL的介绍 ...
- 使用strace工具故障排查的5种简单方法
使用strace工具故障排查的5种简单方法 本文源自5 simple ways to troubleshoot using strace strace 是一个非常简单的工具,用来跟踪可执行程序的系统调 ...
- 深入理解Redis:命令处理流程
Redis是著名的NoSQL键值数据库服务器,为了保证效率,其数据都缓存在内存中.与Memcached相比,Redis支持的数据类型更多,包括String,List,Set,Zset和Hash.下面简 ...
- 利用LM神经网络和决策树去分类
# -*- coding: utf-8 -*- import pandas as pd from scipy.interpolate import lagrange from matplotlib i ...
- hdu 4604 Deque
http://acm.hdu.edu.cn/showproblem.php?pid=4604 将原数组根据其大小关系处理后 使其大小全在10^5内 处理后为 a1,a2,a3.....an 最优deq ...
- windbg调试C#代码(一)
用windbg调试C#代码是比较麻烦的,因为windbg是针对OS层级的,而C#被CLR隔了一层,很多原生的命令如查看局部变量dv.查看变量类型dt等在CLR的环境中都不能用了.必须使用针对CLR的扩 ...
- 用C#操作vss、msbuild、reactor
一.命令行 凡是支持命令行的工具,都可以通过cmd.exe操作.如下: var p = new Process(); p.StartInfo.FileName = "cmd.exe" ...