反射基本函数

reflect.Type 主要提供关于类型相关的信息,所以它和 _type 关联比较紧密;

reflect.Value 则结合 _type data 两者,因此可以获取甚至改变类型的值。

1
2
3
4
5
func TypeOf(i interface{}) Type // 返回i的对象类型
func ValueOf(i interface{}) Value // 返回iValue(如果要对i进行修改的话传入变量地址)
reflect.Indirect(v value) // 返回v获取了该指针指向的值
reflect.New(typ Type) // 返回一个新的type类型的对象。

TypeOf

1
2
3
4
5
6
7
8
9
10
11
12
func TypeOf(i interface{}) Type {
// 将i转成emptyInterface
eface := *(*emptyInterface)(unsafe.Pointer(&i))
// emptyInterface的*rtype实现了Type的接口
return toType(eface.typ)
}

type emptyInterface struct {
// *rtype实现了Type的接口
typ *rtype
word unsafe.Pointer
}

  • 将先将 i 转换成 *emptyInterface 类型,然後在返回typ字段,rtype实现了Type接口

ValueOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
escapes(i)
return unpackEface(i)
}

// 拆解rtype
func unpackEface(i interface{}) Value {
// 将i转成emptyInterface
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函数的返回值,它包含类型结构体指针、真实数据的地址、标志位。

Type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
type Type interface {
// 所有的类型都可以调用下面这些函数

// 此类型的变量对齐后所占用的字节数
Align() int
// 如果是 struct 的字段,对齐后占用的字节数
FieldAlign() int
// 返回类型方法集里的第 `i` (传入的参数)个方法
Method(int) Method
// 通过名称获取方法
MethodByName(string) (Method, bool)
// 获取类型方法集里导出的方法个数
NumMethod() int
// 类型名称
Name() string
// 返回类型所在的路径,如:encoding/base64
PkgPath() string
// 返回类型的大小,和 unsafe.Sizeof 功能类似
Size() uintptr
// 返回类型的大小,和 unsafe.Sizeof 功能类似
String() string
// 返回类型的类型值
Kind() Kind
// 类型是否实现了接口 u
Implements(u Type) bool
// 是否可以赋值给 u
AssignableTo(u Type) bool
// 是否可以类型转换成 u
ConvertibleTo(u Type) bool
// 类型是否可以比较
Comparable() bool
// 类型所占据的位数
Bits() int
// 返回通道的方向,只能是 chan 类型调用
ChanDir() ChanDir
// 返回类型是否是可变参数,只能是 func 类型调用
// 比如 t 是类型 func(x int, y ... float64)
// 那么 t.IsVariadic() == true
IsVariadic() bool
// 返回内部子元素类型,只能由类型 Array, Chan, Map, Ptr, or Slice 调用
Elem() Type
// 返回结构体类型的第 i 个字段,只能是结构体类型调用
// 如果 i 超过了总字段数,就会 panic
Field(i int) StructField
// 返回嵌套的结构体的字段
FieldByIndex(index []int) StructField
// 通过字段名称获取字段
FieldByName(name string) (StructField, bool)
// 返回名称符合 func 函数的字段
FieldByNameFunc(match func(string) bool) (StructField, bool)
// 获取函数类型的第 i 个参数的类型
In(i int) Type
// 返回 map 的 key 类型,只能由类型 map 调用
Key() Type
// 返回 Array 的长度,只能由类型 Array 调用
Len() int
// 返回类型字段的数量,只能由类型 Struct 调用
NumField() int
// 返回函数类型的输入参数个数
NumIn() int
// 返回函数类型的返回值个数
NumOut() int
// 返回函数类型的第 i 个值的类型
Out(i int) Type
// 返回类型结构体的相同部分
common() *rtype
// 返回类型结构体的不同部分
uncommon() *uncommonType
}
  • 使用示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    package main

    import (
    "fmt"
    "reflect"
    )

    type FooIF interface {
    DoSomething()
    DoSomethingWithArg(a string)
    DoSomethingWithUnCertenArg(a ... string)
    }

    type Foo struct {
    A int
    B string
    C struct {
    C1 int
    }
    }

    func (f *Foo) DoSomething() {
    fmt.Println(f.A, f.B)
    }

    func (f *Foo) DoSomethingWithArg(a string) {
    fmt.Println(f.A, f.B, a)
    }

    func (f *Foo) DoSomethingWithUnCertenArg(a ... string) {
    fmt.Println(f.A, f.B, a[0])
    }

    func (f *Foo) returnOneResult() int {
    return 2
    }

    func main() {
    var simpleObj Foo
    var pointer2obj = &simpleObj
    var simpleIntArray = [3]int{1, 2, 3}
    var simpleMap = map[string]string{
    "a": "b",
    }
    var simpleChan = make(chan int, 1)
    var x uint64
    var y uint32

    varType := reflect.TypeOf(simpleObj)
    varPointerType := reflect.TypeOf(pointer2obj)

    // 对齐之后要多少容量
    fmt.Println("Align: ", varType.Align())
    // 作为结构体的`field`要对其之后要多少容量
    fmt.Println("FieldAlign: ", varType.FieldAlign())
    // 类型名称
    fmt.Println("Name: ", varType.Name())
    // 绝对引入路径
    fmt.Println("PkgPath: ", varType.PkgPath())
    // 实际上用了多少内存
    fmt.Println("Size: ", varType.Size())
    // 类型的类型值
    fmt.Println("Kind: ", varType.Kind())
    // 有多少函数(指针对象能获取全部函数,非指针的只能获取非指针函数)
    fmt.Println("NumMethod: ", varPointerType.NumMethod())

    // 通过名字获取一个函数
    m, success := varPointerType.MethodByName("DoSomethingWithArg")
    if success { // 调用该方法
    m.Func.Call([]reflect.Value{
    reflect.ValueOf(pointer2obj),
    reflect.ValueOf("sad"),
    })
    }

    // 通过索引获取函数
    m = varPointerType.Method(1)
    m.Func.Call([]reflect.Value{ // 调用该方法
    reflect.ValueOf(pointer2obj),
    reflect.ValueOf("sad2"),
    })

    // 是否实现了某个接口
    fmt.Println("Implements:", varPointerType.Implements(reflect.TypeOf((*FooIF)(nil)).Elem()))
    // 看看指针多少bit
    fmt.Println("Bits: ", reflect.TypeOf(x).Bits())
    // 查看array, chan, map, ptr, slice的元素类型
    fmt.Println("Elem: ", reflect.TypeOf(simpleIntArray).Elem().Kind())
    // 查看Array长度
    fmt.Println("Len: ", reflect.TypeOf(simpleIntArray).Len())
    // 查看结构体类型的第 i 个字段
    fmt.Printf("Field: %+v \n", varType.Field(1))
    // 查看嵌套的结构体的字段
    fmt.Printf("FieldByIndex %+v \n", varType.FieldByIndex([]int{2, 0}))
    // 通过字段名称获取字段
    fi, success2 := varType.FieldByName("A")
    if success2 {
    fmt.Printf("FieldByName: %+v \n", fi)
    }
    // 查看名称符合 func 函数的字段
    fi, success2 = varType.FieldByNameFunc(func(fieldName string) bool {
    return fieldName == "A"
    })
    if success2 {
    fmt.Printf("FieldByName: %+v \n", fi)
    }
    // 查看结构体数量
    fmt.Println("NumField", varType.NumField())
    // 查看map的key类型
    fmt.Println("Key: ", reflect.TypeOf(simpleMap).Key().Name())
    // 查看函数有多少个参数
    fmt.Println("NumIn: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).NumIn())
    // 查看函数参数的类型
    fmt.Println("In: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).In(0))
    // 查看最后一个参数,是否解构了
    fmt.Println("IsVariadic: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).IsVariadic())
    // 查看函数有多少输出
    fmt.Println("NumOut: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).NumOut())
    // 查看函数输出的类型
    fmt.Println("Out: ", reflect.TypeOf(pointer2obj.returnOneResult).Out(0))
    // 查看通道的方向, 3双向。
    fmt.Println("ChanDir: ", int(reflect.TypeOf(simpleChan).ChanDir()))
    // 查看该类型是否可以比较。不能比较的slice, map, func
    fmt.Println("Comparable: ", varPointerType.Comparable())
    // 查看类型是否可以转化成另外一种类型
    fmt.Println("ConvertibleTo: ", varPointerType.ConvertibleTo(reflect.TypeOf("a")))
    // 该类型的值是否可以另外一个类型
    fmt.Println("AssignableTo: ", reflect.TypeOf(x).AssignableTo(reflect.TypeOf(y)))
    }

value

Value 字段还有很多方法,可以去看文档: http://docscn.studygolang.com/pkg/reflect/,或者去src/reflect/value.go看看源码,搜索 func(v Value) 就能看到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
value.CanSet()返回值能否更改
value.MethodByName("name")//根据方法名查找方法返回Value
value.FieldByName("name")//根据属性名查找属性返回Value

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
func (v Value) IsValid() bool // 是否是一个合法的Value对象 只有零值才会返回false
func (v Value) Kind() Kind //* 所属的类型分类 零值会返回Invalid
func (v Value) String() string//* 字符串格式 返回值的格式为<类型 值>
func (v Value) Type() Type//类型
* 方法集和方法
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) 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
*函数类型值
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
*类型信息
func ChanDir() ChanDir//判断通道的方向
func Elem() Type//元素的类型
*通道值
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
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()]之间
*映射类型
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语言提供了两种指针类型,一种是通过*和其他类型复合而成,另一种是unsafe.Pointer
func Elem() Type
func(v Value) IsNil() bool
func(v Value) Elem() Value
func(v Value) Pointer() uintptr
*数组
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
*接口类型
func(v Value) IsNil() bool//判断接口是否为空
func(v Value) Elem() Value//返回接口包含的真实值
func(v Value) InterfaceData() [2]uintptr//这个方法的用法比较奇怪,还未能找到一个合适的例子
[]byte类型
func (v Value)Bytes() []bytes
func (v Value)SetBytes(x []bytes)
字符串类型
func (v Value) SetString(x string)//设置字符串的值
func (v Value) Index(i int)Value//访问单个字节
func (v Value) Len() int//字符串的长度

StructField

结构的字段具有很多特殊信息,定义了StructField类型来表示一个字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

type StructField struct{
Name string //名称
PkgPath string //* 对于导出字段,为空字符串* 对于非导出字段,是定义该字段类型的包名
Type Type
Tag StructTag
Offset uintptr //在结构内的位移
Index []int //当使用Type.FieldByIndex()方法的参数
Anonymous bool //是否为匿名字段
}
  • StructTag 描述了结构字段的tag

    1
    2
    3
    4
    5
    6
    7
    tag格式为:
    * 由多个部分连接而成,部分之间有可选的空格
    * 部分格式为 key:value
    * key是非空的字符串,由非控制字符组成,并且不可以是空格、双引号、冒号
    * 值由双引号包围,遵循Go字符串字面值语法

    func (tag StructTag) Get(key string) string //将一个tag看做映射,各个部分就是映射的元素
  • Kind

    reflect包使用Kind类型来表示类型所属的分类

反射的三大定律

  1. Reflection goes from interface value to reflection object.

  2. Reflection goes from reflection object to interface value.

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

    1. 反射可以从接口类型到反射类型对象
      • 反射是一种检测存储在 interface 中的类型和值机制。这可以通过 TypeOf 函数和 ValueOf 函数得到。
    1. 反射可以从反射类型对象到接口类
      • 将 ValueOf 的返回值通过 Interface()函数反向转变成 interface 变量
    1. 修改反射类型变量的内部值需要保证其可设置性
      • 如果想要操作原变量,反射变量 Value 必须要使用原变量的地址才行。