Go的reflect机制和reflect包
来源:互联网 发布:知世公主动画出场 编辑:程序博客网 时间:2024/05/02 00:05
Go的reflect机制和reflect包
概述
虽然Go是静态语言,然而还是提供了reflect机制,并且定义了reflect包来辅助反射处理。在reflect包中,最重要的两个类型就是Type和Value,分别从类型、值的角度来描述一个Go对象。
Type类型是一个接口,这个接口实现了String() string方法。Value类型是一个结构,但是并未定义任何导出字段,同样定义了String() string方法。
使用如下语句来导入reflect包
import "reflect"
注意:
1.本文中出现在类型章节的函数,都是Type的方法。由于接口的特殊性,无法明确其receiver是指针还是值,所以并未显示其receiver,但都是有receiver的
2.很多方法都有要求,如果要求不满足,那么panic
3.Value类型也有零值,零值只能调用IsValid()/String()/Kind()方法,其余方法都会panic
下面我们将依次介绍不同类型的对象和它对应的Type/Value对象
Go的reflect机制和reflect包 1
类型 3
值 4
算术类型的Go对象: 6
Go的reflect机制和reflect包 1
类型 3
Kind类型 4
值 4
算术类型的Go对象: 6
类型 6
值 6
结构类型的Go对象 7
类型 7
StructField类型 7
StructTag类型 8
值 8
方法类型的Go对象 8
类型 8
值 9
通道类型的Go对象 9
类型 9
值: 10
Slice类型的Go对象 10
类型 11
值 11
映射类型的Go对象 11
类型 11
值 12
指针类型的Go对象 12
类型 12
值 12
数组类型的Go对象 12
类型 12
值 12
接口类型的Go对象 13
值 13
[]byte类型的Go对象 13
值 13
字符串类型的Go对象 13
值 13
组合生成一个Value 13
组合生成一个Type 15
值 6
结构类型的Go对象 7
类型 7
值 8
方法类型的Go对象 8
类型 8
值 9
通道类型的Go对象 9
类型 9
值: 10
Slice类型的Go对象 10
类型 11
值 11
映射类型的Go对象 11
类型 11
值 12
指针类型的Go对象 12
类型 12
值 12
数组类型的Go对象 12
类型 12
值 12
接口类型的Go对象 13
值 13
[]byte类型的Go对象 13
值 13
字符串类型的Go对象 13
值 13
组合生成一个Value 13
组合生成一个Type 15
所有类型的Go对象:
对于任何类型的Go对象而言,类型和值都是其运行时的相关信息,使用TypeOf()/ValueOf()函数获得表示类型/值信息。
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
类型
Type类型定义了方法来提供如下:
* 对齐信息: 包括作为变量时的对齐要求和作为一个结构字段时的对齐信息
func Align() int
func FieldAlign() int
* 大小: 一个该类型的值存储所需要的内存大小,以字节为单位
func Size() uinptr
* 名称: 该类型在其定义包中的名称,有些类型没有名称(比如数组、slice、函数等等),返回一个空字符串
func Name() string
* 定义位置: 该类型的定义位置,也就是导入该类型使用的import语句的参数。如果该类型是预定义的(string, error等)或者无名的,那么返回一个空字符串
func PkgPath() string
* 种类: 该类型所属的种类,reflect包定义了Kind类型来表示各种类型。重命名一个类型并不会改变其种类
func Kind() Kind
* 方法集: 该类型的方法集,Type类型提供了方法来返回方法数量,访问各个方法。reflect包定义了Method类型来表示一个方法
func NumMethod() int
func Method(index int) Method
//使用索引来访问方法集,索引从0开始,如果越界,将panic
func MethodByName(name string) (Method, bool)
//使用名称来访问某个方法,bool参数指示是否找到该方法
* 是否实现某个接口
func Implements( u Type) bool
//判断是否使用了接口u,u必须表示一个接口类型
* 是否可以使用标准转换语句,转换为其他类型
func ConvertibleTo(u Type) bool
* 是否可以赋值给其他类型的变量
func AssignableTo(u Type) bool
Kind类型
reflect包使用Kind类型来表示类型所属的分类,其定义如下:
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
Kind()类型定义了String() string方法,提供一个字符串表示形式
值
Value类型表示一个Go对象的值,对于任意类型的Go对象,以下方法都是可用的
* 是否可以获得地址。如果一个值来自以下途径,那么可以获得其地址
l Slice的一个元素
l 一个可以获得地址的数组的元素
l 一个可以获得地址的结构的字段
l 解引用一个指针的结果
func (v Value) CanAddr() Value
//这个方法是设置值的方法的基础,使用ValueOf()生成一个Value时,参数是值传递的,因此设置这个参数一点意义也没有。正确的方法是传入一个指针,然后调用Elem()方法来生成其指向的元素对应的Value对象
* 获得其地址
func (v Value) Addr() Value
//如果CanAddr()返回false,那么这个调用会panic
func (v Value) UnsafeAddr() uintptr
//和Addr()方法有同样的要求
* 是否可以修改其值,一个值必须是可以获得地址且不能通过访问结构的非导出字段获得,方可被修改
func (v Value) CanSet() bool
* 设置值
func (v Value) Set(x Value)
//如果CanSet()返回false,那么panic
* 转换为其他类型的值
func (v Value) Convert(t Type) Value
//如果无法使用标准Go转换规则来转换,那么panic
* 以空接口类型获得值
func (v Value) Iterface{} interface{}
//如果Value是通过访问结构的非导出字段获得,panic
* 是否是一个合法的Value对象
func (v Value) IsValid() bool
//只有零值才会返回false
* 所属的类型分类
func (v Value) Kind() Kind
//零值会返回Invalid
* 方法集和方法
func (v Value) NumMethod() int
func (v Value) Method(index int) Value
func (v Value) MethodByName(name string) Value
//Type类型定义了同名方法,但是返回的是类型信息,这里返回的是值信息。Method()方法,如果v没有任何方法集,或者index越界,那么panic。MethodByName()方法,如果没有找到名为name的方法,那么返回一个零值
* 字符串格式
func (v Value) String() string
//返回值的格式为<类型 值>
* 类型
func (v Value) Type() Type
算术类型的Go对象:
类型
对于算术类型,Type类型提供了以下方法
* 位数: 该类型大小,以二进制位为单位
func Bits() int
值
对于算术类型,Value类型特别提供了如下方法来方便处理
* 获得值
func (v Value) Float() float64
//所有的浮点数类型都是使用这个方法
func (v Value) Int() int64
//所有的有符号整数类型,都是使用这个方法
func (v value) Unt() uint64
//所有的无符号整数类型,都是使用这个方法
func (v Value) Complex() complex128
//所有的复数类型,都是使用这个方法
* 设置值:
func (v Value) SetFloat(x float64)
//所有的浮点数类型都是使用这个方法
func (v Value) SetInt(x int64)
//所有的有符号整数类型,都是使用这个方法
func (v value) SetUnt(x uint64)
//所有的无符号整数类型,都是使用这个方法
func (v Value) SetComplex(x complex128)
//所有的复数类型,都是使用这个方法
* 辅助设置值:由于每个Setxxx方法都是对应了多个具体的基本类型,因此需要一个方法来判断设置值是否能够长度,通过判断值是否可以存储在Valud对象中且不溢出
func (v Value) OverflowFloat(x float64) bool
//所有的浮点数类型都是使用这个方法
func (v Value) OverflowInt(x int64) bool
//所有的有符号整数类型,都是使用这个方法
func (v value) OverflowUnt(x uint64) bool
//所有的无符号整数类型,都是使用这个方法
func (v Value) OverflowComplex(x complex128) bool
//所有的复数类型,都是使用这个方法
//如果使用某个导致溢出的值来调用Setxxx()方法
结构类型的Go对象
类型
结构对象的类型就是字段的数量和类型,但结构的字段具有很多特殊信息,reflect包定义了StructField类型来表示一个字段
func NumField() int
//结构字段数量
func Field(i int) StructField
//使用索引来访问字段,索引从0开始,如果越界将panic
func FieldByName(name string) (StructField,bool)
//使用名称来访问字段,如果未找到那么返回false
func FieldByNameFunc(match func(string) bool) (StructField,bool)
//访问名称使得match函数返回true的字段,在同一个内嵌层次上,只能有一个字段使得match返回true。如果同一层次上多个字段使得match返回true,那么这些字段都认为是不符合要求的
func FieldByIndex(index []int) StructField
//这个方法使得访问结构的内嵌字段成为可能。将访问各个层次的字段的索引排列起来,就形成了一个[]int,参数index不可越界,否则panic
StructField类型
StructField是一个结构,其定义和含义如下:
type StructField struct{
Name string //名称
PkgPath string
Type Type
Tag StructTag
Offset uintptr //在结构内的位移
Index []int //当使用Type.FieldByIndex()方法的参数
Anonymous bool //是否为匿名字段
}
PkgPath字段需要做一些解释:
* 对于导出字段,为空字符串
* 对于非导出字段,是定义该字段类型的包名
StructTag类型
StructTag类型也是一个结构,描述了结构字段的tag。按照约定,tag格式为:
* 由多个部分连接而成,部分之间有可选的空格
* 部分格式为 key:value
* key是非空的字符串,由非控制字符组成,并且不可以是空格、双引号、冒号
* 值由双引号包围,遵循Go字符串字面值语法
定义如下:
type StructTag string
func (tag StructTag) Get(key string) string
//将一个tag看做映射,各个部分就是映射的元素
值
Value类型为结构类型的Go对象几乎定义了相同的方法,唯一的不同是这些方法返回的是一个Value,而不是StructField。Value类型有零值,因此返回bool值就没有意义了
func (v Value) NumField() int
//结构字段数量
func (v Value) Field(i int) Value
//使用索引来访问字段,索引从0开始,如果越界将panic
func (v Value) FieldByName(name string) Value
//使用名称来访问字段,如果未找到那么返回false
func (v Value)FieldByNameFunc(match func(string) bool) Value
//访问名称使得match函数返回true的字段,在同一个内嵌层次上,只能有一个字段使得match返回true。如果同一层次上多个字段使得match返回true,那么这些字段都认为是不符合要求的
func FieldByIndex(index []int) Value
//这个方法使得访问结构的内嵌字段成为可能。将访问各个层次的字段的索引排列起来,就形成了一个[]int,参数index不可越界,否则panic
方法类型的Go对象
类型
对于一个函数类型,所有的信息都在其签名中,那就是参数的数量、类型以及返回值的数量、类型。
Type类型定义了如下方法,提供这些信息
func IsVariadic() bool
//参数是否可变
func NumIn() int
func NumOut() int
//参数、返回值的数量,需要注意的是,可变参数单独作为slice计算
func In(i int) Type
func Out(i int) Type
//第i个参数/返回值,i从0开始
值
对于一个函数类型值,Value类型提供了2个方法来调用这个值,也就是调用函数。
在reflect包中,Value类型被定义为一个结构,并且没有任何的导出字段,所有的信息都是通过方法调用给出的。
func (v Value) Call(in []Value)[]Value
func (v Value) CallSlice(in []Value) []Value
Call()方法用来调用函数(参数可变或者固定),采用的是用户代码使用的调用格式。CallSlice()方法专门用于调用参数可变的函数,它采用了编译器使用的调用格式。这两种调用格式的区别在于:
u 对于参数固定的函数,两种格式没有任何区别,都是按照位置,将实参赋予形参
u 对于参数可变的函数,编译器格式会特别处理最后一个参数,将剩余的实参依次放入一个slice内,传递给可变形参的就是这个slice。
还有一个比较特殊的方法
func (v Value) Pointer() uintptr
//以uintptr返回函数的值,这个值并不能独一无二的识别一个函数,只是保证如果函数为nil,那么这个值为0
通道类型的Go对象
Go语言的通道对象信息可以分为两类:
l 静态信息: 通道的方向和元素类型
l 动态信息: 发送、接收,通道的容量和当前元素数量
类型
Value对象定义了了以下方法来获得通道类型的类型信息
func ChanDir() ChanDir
//判断通道的方向
func Elem() Type
//元素的类型
ChanDir
ChanDir表示通道的方向,其定义如下
type ChanDir int
func (chan ChanDir)String() string
reflect包定义了一组常量来表示各种方向
const (
RecvDir ChanDir =1<<iota
SendDir
BothDir
)
值:
对于一个通道值,Value定义了如下方法
func (v Value) IsNil() bool
func (v Value) Pointer() uintptr
//以unitptr返回其值,没有使用unsafe.Pointer类型,所以不需要导入unsafe包
func (v Value) Close()
func (v Value) Len() int
//通道当前元素数量
func (v Value) Cap() int
//通道的长度
func (v Value) Send(x Value)
//发送一个值,x必表示一个可以赋值给通道元素类型的值
func (v Value) TrySend(x Value) bool
//尝试以非阻塞的方式发送一个值,返回操作成功与否
func (v Value) Recv() (Value,bool)
//接收一个值,如果通道已经关闭,返回一个Value零值。由于通道本身可能传输Value零值,所以必须额外使用一个布尔返回值来表示接收是否成功
func (v Value) TryRecv() (Value,bool)
//尝试以非阻塞的方式接收一个值
Slice类型的Go对象
在1.2版本中,每个Slice的真实类型为
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
在官方文档中这样写明:这个格式可能会在后续的版本中改变。Data字段并不足够确保它引用的数据不会被回收,程序应该以正确的格式来另外保存一个指向底层数据的指针
Go语言的slice对象信息可以分为两类:
n 静态信息: 元素类型
n 动态信息: 长度和容量
类型
func Elem() Type
值
func (v Value) Len() int
func (v Value) Cap() int
func (v Value) IsNil() bool
func (v Value) Pointer() uintptr
func (v Value) Index(i int) Value
//访问某个元素
func (v Value) Slice(i,j int) Value
//访问某个子slice,下标必须合法
func (v Value) Slice3(i,j,k) Value
//以Go1.3引入的3下标格式访问某个子slice,下标必须合法
func (v Value) SetCap(i int)
//要求i必须在[v.Len(),v.Cap()]之间
func (v Value) SetLen(i int)
//i必须在[0,v.Cap()]之间
映射类型的Go对象
映射类型的Go对象信息可以分为两类:
l 静态信息: 键类型和元素类型
l 动态信息: 键值、元素值以及映射的大小
类型
func Key() Type
func Elem() Type
值
func (v Value) Len() int
func (v Value) IsNil() bool
func (v Value) MapKeys() []Value
//返回所有的键值
func (v Value) MapIndex(key Value) Value
func (v Value) SetMapIndex(key, x Value)
//如果x是零值,那么表示删除一个元素
func (v Value) Pointer() uintptr
指针类型的Go对象
Go语言提供了两种指针类型,一种是通过*和其他类型复合而成,另一种是unsafe.Pointer
类型
func Elem() Type
值
func(v Value) IsNil() bool
func(v Value) Elem() Value
func(v Value) Pointer() uintptr
数组类型的Go对象
类型
func Elem() Type
func Len() int
值
func(v Value) Len() int
func(v Value) Slice(i,j int) Value
func(v Value) Slice3(i, j, k int) Value
//这两个方法要求v.CanAddr()返回true
接口类型的Go对象
值
func(v Value) IsNil() bool
//判断接口是否为空
func(v Value) Elem() Value
//返回接口包含的真实值
func(v Value) InterfaceData() [2]uintptr
//这个方法的用法比较奇怪,还未能找到一个合适的例子
[]byte类型的Go对象
[]byte类型应用非常广泛,几乎所有涉及到数据的操作都会使用这个类型。Value类型特别为这个类型定义了相关操作
值
func (v Value)Bytes() []bytes
func (v Value)SetBytes(x []bytes)
字符串类型的Go对象
值
func (v Value) SetString(x string)
//设置字符串的值
func (v Value) Index(i int)Value
//访问单个字节
func (v Value) Len() int
//字符串的长度
组合生成一个Value
当我们已有某个Value值,可以使用以下方法来组合生成新的Value对象
func Append(s Value,value ...Value) Value
func AppendSlice(s, t Value) Value
* 前者将value参数附加到s参数中,返回结果
* 后者将t参数的元素附加到s参数末尾,返回结果
* 很显然,以值传递参数,参数本身的值并未改变
* s,t 必须都是表示slice,并且遵循Go的slice规则,即元素类型必须可赋值(前者)/相同(后者)
func Indirect(s Value) Value
* 如果s表示一个指针,那么返回所指向的元素,特别地,nil指针返回一个零值的Value
* 如果s表示其他类型值,那么返回s本身
或者使用一个Type类型,生成表示该类型值的Value,当然生成的都是零值
func MakeChan(typ Type, buffer int) Value
生成一个通道
func MakeMap(typ Type) Value
生成一个映射
func MakeSlice(typ Type, len, cap int) Value
生成一个slice
func New(typ Type) Value
生成一个类型为typ的零值,返回的是其指针,符合New的语义
func NewAt(typ Type, p unsafe.Pointer) Value
将p当做一个指向typ类型的指针,包装并返回
func Zero(typ Type) Value
生成typ类型的零值,这里其实是返回特殊值,返回值既不可以修改也不可以取得其地址
func MakeFunc(typ Type, fn func(args []Value)(results []Value)) Value
从某个函数类型typ生成一个Value,返回值wrap了第二个参数。返回值的类型为typ,但真实操作却由fn完成。返回值在调用时,会发生以下操作:
1.将所有参数转换为Value,并且加入到fn的args中。特别地,可变参数会先转换为一个slice,然后作为args的最后一个参数加入。
2.调用fn
3.将fn的返回值转换为typ中描述的类型,并返回
fn的签名类型非常特殊,允许任何数量、类型的参数,返回任何数量、类型的返回值,因此任何函数都可以包装在这样的签名里。真实的签名由typ提供,从而提供了动态生成函数的机制。
go文档提供了一个很好的例子,但这个函数的应用非常之广泛,就靠大家来探索了。
组合生成一个Type
当我们已经有一个Type值,可以使用以下方法来组合生成一个新的Type对象
func SliceOf(t Type) Type
func ChanOf(dir ChanDir, t Type) Type
func MapOf(key, value Type) Type
func PtrTo(t Type) Type
这些函数都非常简单,不需要什么特殊说明
- Go的reflect机制和reflect包
- Go Reflect
- go-reflect
- go reflect
- Go Reflect
- Go语言学习之reflect包(The way to go)
- reflect
- reflect
- Reflect
- Reflect
- Reflect
- Reflect
- Reflect
- Reflect
- Reflect
- reflect
- Reflect
- reflect
- 原创】OllyDBG 入门系列(一)-认识OllyDBG
- 各数据库相关文件(*.sql,*.mdb,*.myd,*.bak等)知识
- IOS面经系列(二)什么是id ,为什么要用id,在哪用id
- 模拟行情服务器
- Java正则表达式的完全匹配与部分匹配
- Go的reflect机制和reflect包
- 一个小男孩的飞鸽传书
- 原来代码这样发的
- Java之美[从菜鸟到高手演变]之数据结构基础之树、二叉树
- 操作系统课堂笔记(2)操作系统的硬件环境之缓冲技术、中断技术
- bash与sh的区别
- oracle百问6
- Objective-C基本数据类型整理
- Java之美[从菜鸟到高手演变]之数据结构基础、线性表、栈和队列、数组和字符串