Go语言反射(二)
# 1、什么是反射
反射是程序在运行时检查其变量和值并找到其类型的能力.反射的三大原则如下
反射三原则
- 反射从界面值到反射对象.
- 反射从反射对象到接口值.
- 若要修改反射对象,该值必须是可设置的.
# 2、反射的特点
反射的特点
- 反射功能具有强大的功能
- 反射是用程序检查其所拥有的结构,尤其是类型的一种能力
- 是元编程的一种形式
- 可以在[运行时]通过反射来分析一个结构体
- 检查其类型和变量(类型和取值)和方法
- 动态的修改变量和调用方法
- 这对于没有源代码的包尤其有用
- 这是一个强大的工具,除非真的有必要,否则应当避免使用或者小心使用
# 3、reflect
包
在Go语言中,
reflect
实现了运行时反射.reflect
包会帮助识别interface{}
变量的底层具体类型和具体值.reflect
包有两个数据类型如下:
- Type:数据类型 【
reflect.TypeOf():
是获取Type
的方法】- Value:值的类型【
reflect.ValueOf():
是获取Value
的方法】- 官方反射文档: https://golang.org/pkg/reflect/ (opens new window)
# reflect.Kind
reflect.Type
和reflect.Value
公用的Kind类型说明
//Kind代表具体的特定类型.
//Invalid不是有效类型.
type Kind uint
const (
Invalid Kind = iota // 非法类型
Bool // 布尔型
Int // 有符号整型
Int8 // 有符号8位整型
Int16 // 有符号16位整型
Int32 // 有符号32位整型
Int64 // 有符号64位整型
Uint // 无符号整型
Uint8 // 无符号8位整型
Uint16 // 无符号16位整型
Uint32 // 无符号32位整型
Uint64 // 无符号64位整型
Uintptr // 指针
Float32 // 单精度浮点数
Float64 // 双精度浮点数
Complex64 // 64位复数类型
Complex128 // 128位复数类型
Array // 数组
Chan // 通道
Func // 函数
Interface // 接口
Map // 映射
Ptr // 指针
Slice // 切片
String // 字符串
Struct // 结构体
UnsafePointer // 底层指针
)
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
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
# 4、reflect.Type
接口详解
# Type接口方法汇总
type Type interface {
//适用于所有类型的方法.类型在内容分配时,返回的字节的对齐方式
Align() int
// 当类型作为一个结构的字段时,他的字节对齐方式
FieldAlign() int
// 方法返回类型的方法集中的第i个方法.
// 如果我不在[0,NumMethod()]范围内,它会报错.
// 对于非接口类型T或*T,返回的方法的类型和函数
// 字段描述第一个参数是接收器的函数.
// 对于接口类型,返回方法的type字段提供者
// 通过整形索引,反射类型的方法,返回一个Method类型
Method(int) Method
// 通过方法名,判断该类型是否有该方法,如果存在返回该方法,并且返回ok值为true
MethodByName(string) (Method, bool)
// 返回该类型拥有的外部可调用的方法数量(方法名称为英文字母大写开头的)
NumMethod() int
// 返回类型的名称,如果是匿名类型将返回空字符窜.
// 如果是数组、切片、Map、指针等类型,则什么都没有
Name() string
//返回类型所在包的路径,如果是指针则什么都木有
PkgPath() string
// 返回存储所需的字节数
// 给定类型的值;类似于unsafe.Sizeof.
Size() uintptr
// 返回该类型的字符串表示形式.
//字符串表示可以使用缩短的包名称(例如,base64而不是"encoding/base64")
String() string
// 返回这个类型的种类,(比如struct,slice,ptr,func等)
Kind() Kind
// 判断类型是否实现u这个接口.注意u必须不能为nil,且是一个接口
Implements(u Type) bool
// 判断类型是否可分配到u类型
AssignableTo(u Type) bool
// 判断该类型的值是否可以转换为u类型
ConvertibleTo(u Type) bool
// 判断此类型的值是否可比较.
Comparable() bool
//方法仅适用于某些类型,具体取决于类型
//每种方法都允许:
//
// Int*, Uint*, Float*, Complex*: Bits
// Array: Elem, Len
// Chan: ChanDir, Elem
// Func: In, NumIn, Out, NumOut, IsVariadic.
// Map: Key, Elem
// Ptr: Elem
// Slice: Elem
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
//返回反射类型的使用的大小(以位为单位),如果不是Int,Uint,Float,complex种类则会产生恐慌
Bits() int
// 返回反射channel的目录,如果该类型不是chan则会出现异常
ChanDir() ChanDir
// 判断函数是否有可变参数(...)
IsVariadic() bool
//Elem返回类型的元素类型.
//如果类型的类型不是Array、Chan、Map、Ptr或Slice,则会出现恐慌.
Elem() Type
//返回结构类型的第i个字段.值为StructField类型
//如果类型的类型不是Struct,它就会恐慌.
//如果不在[0,NumField()]范围内,它会惊慌失措.
Field(i int) StructField
// 返回对应的嵌套字段
//如果类型的类型不是Struct,它就会恐慌.
FieldByIndex(index []int) StructField
//FieldByName返回具有给定名称的结构字段
//以及一个布尔值,指示是否找到该字段.
FieldByName(name string) (StructField, bool)
//FieldByNameFunc返回具有名称的结构字段,以及一个布尔值,指示是否找到该字段
//FieldByNameFunc考虑结构本身中的字段,然后是任何嵌入结构中的字段,以宽度优先的顺序,
//停在最浅的嵌套深度,包含一个或多个,满足匹配函数的字段.
FieldByNameFunc(match func(string) bool) (StructField, bool)
//返回函数类型的第i个输入参数的类型.
//如果类型的类型不是Func,它就会恐慌.
//如果我不在[0,NumIn()]范围内,它会惊慌失措.
In(i int) Type
// 反射map类型的key的类型,如果不是map则会产生恐慌
Key() Type
// 返回反射array类型的长度,如果不是array,则会产生恐慌
Len() int
// 返回反射一个struct的字段数量,如果不是struct则会产生恐慌
NumField() int
// 反射一个func的输入参数的数量,如果不是函数则会产生恐慌
NumIn() int
//NumOut返回函数类型的输出参数计数.
//如果类型的类型不是Func,它就会恐慌.
NumOut() int
//Out返回函数类型的第i个输出参数的类型.
//如果类型的类型不是Func,它就会恐慌.
//如果我不在[0,NumOut()范围内,它会惊慌失措.
Out(i int) Type
//返回*rtype结构,该指针是大多数值的常见实现.
//它嵌入到其他结构类型中.
common() *rtype
//返回*uncommonType类型,该类型只存在于定义的类型或具有方法的类型
//(如果T是定义的类型,则T和*T的unmontypes有方法).
//使用指向此结构的指针可以减少所需的总体大小
//描述没有方法的未定义类型.
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
130
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
130
# Method
结构
MethodByName()
和Method()
会返回这种类型,结构如下
type Method struct {
Name string //Name是方法名.
PkgPath string //方法的路径
Type Type // 方法类型
Func Value // 以接收者为第一个参数
Index int // 方法索引
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# StructField
结构
Field()
和FieldByIndex()
,以及FieldByName()
,FieldByNameFunc
会返回该类型
//描述结构中的单个字段.
type StructField struct {
Name string // 字段名称
PkgPath string // 字段在结构体中的路径,限定小写字母开头字段名(未报告)包路径.大写字母开头的包路径为空.
Type Type // 字段本身的反射类型对象,类型为 reflect.Type,可以进一步获取字段的类型信息
Tag StructTag // 字段标签字符串
Offset uintptr // 结构中的偏移量(字节)
Index []int // Type.FieldByIndex的索引序列
Anonymous bool // 是否匿名字段
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 类型函数介绍
//返回channel type
func ChanOf(dir ChanDir, t Type) Type
//返回Map type
func MapOf(key, elem Type) Type
//返回指针类型
func PtrTo(t Type) Type
//返回slice类型
func SliceOf(t Type) Type
//反射变量类型,最好不要直接传指针进去.否则会有些东西发射不出.例如Name()
func TypeOf(i interface{}) Type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//举例说明如下
type Student struct {
User User
Name string
Age string
}
type User struct {
UserName string
Password string
Address string
}
func main (){
stu:=Student{
User: User{
UserName: "ourlang",
Password: "123456",
Address: "成都",
},
Name: "福小林",
Age: "18",
}
typeOf := reflect.TypeOf(stu)
//返回stu的第二个字段Name
//{Name string 48 [1] false}
fmt.Println(typeOf.FieldByIndex([]int{1}))
//返回stu的第二个字段Name 因为Name不是结构体 你再去取Name的第一个字段就会报错
//panic: reflect: Field of non-struct type
//fmt.Println(typeOf.FieldByIndex([]int{1,0}))
//返回stu的第一个字段User结构体中的第一个字段UserName
//{UserName string 0 [0] false}
fmt.Println(typeOf.FieldByIndex([]int{0,0}))
//返回stu的第一个字段User结构体中的第三个字段Address
//{Address string 32 [2] false}
fmt.Println(typeOf.FieldByIndex([]int{0,2}))
}
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
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
# Type示例一
特别说明
func TypeOf(i interface{}) Type
//Type
是interface{}
的别名- 数组、切片、
Map
、指针等类型的变量,它们的.Name()
都是返回空. Elem()
返回类型的元素类型,如果类型的类型不是Array
、Chan
、Map
、Ptr
或Slice
,则会出现恐慌.
package main
import (
"fmt"
"reflect"
)
type myInt int64
type User struct {
UserName string `json:"userName"`
Password string `json:"password"`
Age int `json:"age"`
Address string `json:"address"`
flag bool `json:"flag"`
}
//func TypeOf(i interface{}) Type //Type是interface{}的别名
//获取数据的类型和种类
func GetReflectType(x interface{}) {
t := reflect.TypeOf(x)
//特别注意: 数组、切片、Map、指针等类型的变量,它们的.Name()都是返回空.
fmt.Printf("type:%v kind:%v\n", t.Name(), t.Kind())
}
func main() {
var a *float32 // 指针
var b myInt // 自定义类型
var c rune // 类型别名
GetReflectType(a) // type: kind:ptr
GetReflectType(b) // type:myInt kind:int64
GetReflectType(c) // type:int32 kind:int32
u1 := User{
UserName: "福小林",
Password: "123456",
Age: 28,
Address: "成都市",
flag: true,
}
GetReflectType(u1) //type:User kind:struct
}
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
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
# Type示例二
配合
reflect.StructField
、reflect.Method
、reflect.Type
等的相关方法进行举例,更多Type使用方法参考上面type接口方法汇总
、method结构
、structfield结构
package main
import (
"fmt"
"reflect"
)
type User struct {
UserName string `json:"userName"`
Password string `json:"password"`
Age int `json:"age"`
Address string `json:"address"`
flag bool `json:"flag"`
}
//获取数据的类型和种类
func GetReflectType(x interface{}) {
t := reflect.TypeOf(x)
//特别注意: 数组、切片、Map、指针等类型的变量,它们的.Name()都是返回空.
fmt.Printf("type:%v kind:%v\n", t.Name(), t.Kind())
}
func (u *User) GetPassword() string {
return u.Password
}
func (u User) Say(msg string) {
fmt.Println(u.UserName + msg)
}
func main() {
u1 := User{
UserName: "福小林",
Password: "123456",
Age: 28,
Address: "成都市",
flag: true,
}
stuType := reflect.TypeOf(u1) //获取结构体的类型
//该类型的方法个数
numMethod := stuType.NumMethod()
for i := 0; i < numMethod; i++ {
//也可以通过MethodByName查找类型对应的方法
//method, b := stuType.MethodByName("Say")
myMethod := stuType.Method(i)
//方法名 方法类型 方法以接收者为第一个参数
fmt.Println(myMethod.Name, myMethod.Type, myMethod.Func)
}
GetReflectType(u1) //type:User kind:struct
//如果u1是结构体指针,则需要进行解耦elem(),否则会出现恐慌
//numField := stuType.Elem().NumField()
numField := stuType.NumField()
for i := 0; i < numField; i++ {
//也可以通过FieldByName来找对应结构体字段
//structField, b := stuType.FieldByName("UserName")
//如果u1是结构体指针,则需要进行解耦elem(),否则会出现恐慌
//field := stuType.Elem().Field(i)
field := stuType.Field(i)
// 字段名称和字段类型
fmt.Println(field.Name, field.Type)
//是否是匿名字段和在结构体的索引位置
fmt.Println(field.Anonymous, field.Index)
// 结构中的偏移量(字节)和 字段标签字符串
fmt.Println(field.Offset, field.Tag)
//字段在结构体中的路径,限定小写字母开头字段名(未报告)包路径.大写字母开头的包路径为空.
//这里只有flag字段有包路径
fmt.Println(field.PkgPath)
}
}
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
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
# 5、reflect.Value
详解
# Value结构体的方法
//通常用于获取一个指向结构体字段或slice元素,为了调用一个方法,需要一个指针接收.
func (v Value) Addr() Value
//返回底层的值,如果v的kind不是bool则会产生恐慌
func (v Value) Bool() bool
//返回底层的值,如果v的底层值不是一个字节切片,则会产生恐慌
func (v Value) Bytes() []byte
//检查v是否是可寻址的
func (v Value) CanAddr() bool
// 检查值是否可被设置,只有可寻址的才能被设置
func (v Value) CanSet() bool
// 反射函数的值.并调用
func (v Value) Call(in []Value) []Value
//同Call
func (v Value) CallSlice(in []Value) []Value
//关闭channel,如果不是chan则产生恐慌
func (v Value) Close()
//返回底层的值,如果值不是一个复数,则产生一个恐慌
func (v Value) Complex() complex128
//返回v包含的值,多被用于设置值时的寻址操作
func (v Value) Elem() Value
//返回结构中索引字段的Value
func (v Value) Field(i int) Value
//同上不过.提供的是一个切片
func (v Value) FieldByIndex(index []int) Value
//通过字段名查找结构中的值
func (v Value) FieldByName(name string) Value
//通过函数名查找
func (v Value) FieldByNameFunc(match func(string) bool) Value
//返回底层的值,如果值不是一个float,则产生一个恐慌
func (v Value) Float() float64
//如果kind不是array或者sliece则产生恐慌,将其中的元素返回为Value
func (v Value) Index(i int) Value
//返回底层的值,如果值不是一个int,则产生一个恐慌
func (v Value) Int() int64
//如果接口能被使用,则返回true
func (v Value) CanInterface() bool
//返回V作为interface{}的当前值
func (v Value) Interface() (i interface{})
//如果kind不是一个接口则会产生恐慌
func (v Value) InterfaceData() [2]uintptr
//返回值是否为 nil.如果值类型不是通道(channel)、函数、接口、map、指针或 切片时发生 panic,类似于语言层的v== nil操作
func (v Value) IsNil() bool
//判断值是否有效.当值本身非法时,返回false,例如reflect Value不包含任何值,值为 nil 等.
func (v Value) IsValid() bool
//返回v的种类
func (v Value) Kind() Kind
//返回v的长度
func (v Value) Len() int
//如果是一个map,根据key反射其键值的Value
func (v Value) MapIndex(key Value) Value
//返回map的所有key
func (v Value) MapKeys() []Value
//按索引反射结构某个方法的值
func (v Value) Method(i int) Value
//统计结构方法数量
func (v Value) NumMethod() int
//反射方法的值根据方法名
func (v Value) MethodByName(name string) Value
//反射一个结构的字段数
func (v Value) NumField() int
//覆盖复数
func (v Value) OverflowComplex(x complex128) bool
//覆盖浮点数
func (v Value) OverflowFloat(x float64) bool
func (v Value) overflowFloat32(x float64) bool
func (v Value) OverflowInt(x int64) bool
func (v Value) OverflowUint(x uint64) bool
//反射一个指针的值.返回一个指针的整型值
func (v Value) Pointer() uintptr
//用于channel接收
func (v Value) Recv() (x Value, ok bool)
//用于channel发送
func (v Value) Send(x Value)
//如果v可设置,则设置一个v的值
func (v Value) Set(x Value)
//如果v可设置,且是bool,则设置一个v的值
func (v Value) SetBool(x bool)
func (v Value) SetBytes(x []byte)
func (v Value) SetComplex(x complex128)
func (v Value) SetFloat(x float64)
func (v Value) SetInt(x int64)
func (v Value) SetLen(n int)
func (v Value) SetMapIndex(key, val Value)
func (v Value) SetUint(x uint64)
func (v Value) SetPointer(x unsafe.Pointer)
func (v Value) SetString(x string)
func (v Value) Slice(beg, end int) Value
//如果底层是slice.则返回值.
func (v Value) String() string
//如果底层是字符串.则返回字符串
func (v Value) TryRecv() (x Value, ok bool)
//用于channel,接受
func (v Value) TrySend(x Value) bool
//用于channel,发送
func (v Value) Type() Type
//返回type
func (v Value) Uint() uint64
//如果底层是Uint.则返回uint
func (v Value) UnsafeAddr() uintptr
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
130
131
132
133
134
135
136
137
138
139
140
141
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
130
131
132
133
134
135
136
137
138
139
140
141
# Value方法初体验
package main
import (
"fmt"
"reflect"
)
func GetReflectValue(x interface{}) {
v := reflect.ValueOf(x) //reflect.Value
k := v.Kind() //获取值得种类
switch k {
case reflect.Int64:
// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
case reflect.Float32:
// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
case reflect.Float64:
// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
}
}
func main() {
var a float32 = 3.14
var b int64 = 100
GetReflectValue(a) // type is float32, value is 3.140000
GetReflectValue(b) // type is int64, value is 100
// 将int类型的原始值转换为reflect.Value类型
c := reflect.ValueOf(10)
fmt.Printf("type c :%T\n", c) // type c :reflect.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
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
# Value修改目标对象
特别注意
ValueOf()
传入一个变量的地址,返回是变量的地址- 函数参数传递的是值拷贝,必须传递变量地址才能修改变量值.
- 反射中使用
Elem()
方法来获取指针对应的值,比如结构体reflect.ValueOf(&变量名).Elem()
package main
import (
"fmt"
"reflect"
)
type User struct {
UserName string `json:"userName"`
Password string `json:"password"`
Age int `json:"age"`
Address string `json:"address"`
flag bool `json:"flag"`
}
func main() {
u1 := User{
UserName: "福小林",
Password: "123456",
Age: 28,
Address: "成都市",
flag: true,
}
str := "hello world"
//1 修改普通类型
reflect.ValueOf(&str).Elem().SetString("张三")
fmt.Println(str) //张三
//2 修改结构体
//2.1 ValueOf()传入一个变量的地址,返回是变量的地址
userValue := reflect.ValueOf(&u1)
//reflect.Value &main.User{UserName:"福小林", Password:"123456", Age:28, Address:"成都市", flag:true}
fmt.Printf("%T %#v\n", userValue, userValue)
//2.2 Elem(): 返回的是变量的原始值
elem := userValue.Elem()
//reflect.Value main.User{UserName:"福小林", Password:"123456", Age:28, Address:"成都市", flag:true}
fmt.Printf("%T %#v\n", elem, elem)
//2.3 通过FieldByName(): 传入结构体字段名称 SetString():传入你要修改的变量值
elem.FieldByName("UserName").SetString("李四")
elem.FieldByName("Age").SetInt(19)
elem.FieldByName("Password").Set(reflect.ValueOf("admin"))
fmt.Println(u1) //{李四 admin 19 成都市 true}
}
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
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
# 动态调用方法
说明
- 1、无参调用
reflect.ValueOf(变量名).MethodByName(方法名).Call([]reflect.Value{})
reflect.ValueOf(变量名).MethodByName(方法名).Call(make([]reflect.Value, 0))
- 2、有参调用
reflect.ValueOf(变量名).MethodByName(方法名).Call([]reflect.Value{reflect.ValueOf("第一个参数"), reflect.ValueOf("第二个参数")})
- 3、反射调用struct的方法必须是公有的,反射调用无参方法时必修传 nil 或者 []reflect.Value{}
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string `json:"name"`
UserName string `json:"userName"`
Password string `json:"password"`
Age int `json:"age"`
Address string `json:"address"`
flag bool `json:"flag"`
}
func (u User) Say() {
fmt.Println(u.Name + "说话了")
}
func (u User) Eat(food string) {
fmt.Println(u.Name + "吃的是" + food)
}
func (u User) Entertain(otherName string, food string) {
fmt.Println(u.Name + "请" + otherName + "吃了" + food)
}
func main() {
u1 := User{
Name: "福小林",
UserName: "ourlang",
Password: "123456",
Age: 10,
}
//无参数方法调用
reflect.ValueOf(&u1).MethodByName("Say").Call([]reflect.Value{}) //福小林说话了
reflect.ValueOf(u1).MethodByName("Say").Call(make([]reflect.Value, 0)) //福小林说话了
//有参数方法调用,请注意顺序
reflect.ValueOf(u1).MethodByName("Eat").Call([]reflect.Value{reflect.ValueOf("苹果")}) //福小林吃的是苹果
reflect.ValueOf(u1).MethodByName("Entertain").Call([]reflect.Value{reflect.ValueOf("安凌阳"), reflect.ValueOf("牛排")}) //福小林请安凌阳吃了牛排
}
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
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
# 从反射值对象获取被包装的值
反射不仅可以获取值的类型信息,还可以动态地获取或者设置变量的值.Go语言中使用
reflect.Value
获取和设置变量的值. 变量、interface{}和reflect.Value是可以相互转换的.如图所示
package main
import (
"fmt"
"reflect"
)
func main() {
//声明字符串变量str并赋初值
str := "helloWorld"
//获取变量str的反射值对象
strValue := reflect.ValueOf(str)
//获取interface{}类型的值
strInterface := strValue.Interface()
//打印对应的类型
fmt.Printf("%T %T \n", strValue, strInterface) //reflect.Value string
//通过类型断言转换成字符串
conStr := strInterface.(string)
fmt.Printf("%T %+v \n", conStr, conStr) //string helloWorld
fmt.Println(strInterface) //helloWorld
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 判断反射值为空和有效性
IsNil()
和IsValid()
-- 判断反射值的空和有效性,在结构体字段赋值时特别有用
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string `json:"name"`
UserName string `json:"userName"`
Password string `json:"password"`
Age int `json:"age"`
Address string `json:"address"`
flag bool `json:"flag"`
}
func (u User) Say() {
fmt.Println(u.Name + "说话了")
}
func main() {
//*int的空指针
var a *int
fmt.Println("var a *int:", reflect.ValueOf(a).IsNil())
//nil值
fmt.Println("nil:", reflect.ValueOf(nil).IsValid())
//*int类型的空指针
fmt.Println("(*int)(nil):", reflect.ValueOf((*int)(nil)).Elem().IsValid())
//实例化一个结构体
u1 := User{
Name: "福小林",
UserName: "ourlang",
Password: "123456",
Age: 10,
}
//尝试从结构体中查找一个不存在的字段
fmt.Println("不存在的结构体成员:", reflect.ValueOf(u1).FieldByName("").IsValid())
//尝试从结构体中查找一个不存在的方法
fmt.Println("不存在的方法:", reflect.ValueOf(u1).MethodByName("").IsValid())
fmt.Println("存在的方法Say():", reflect.ValueOf(u1).MethodByName("Say").IsValid())
//判断Name字段是否存在,然后就想修改对应的字段值
byName := reflect.ValueOf(u1).FieldByName("Name")
if byName.IsValid() {
reflect.ValueOf(&u1).Elem().FieldByName("Name").SetString("蜡笔小新")
}
fmt.Println(u1)
//实例化一个map
m := map[int]int{}
//尝试从map中查找一个不存在的键
fmt.Println("不存在的键:", reflect.ValueOf(m).MapIndex(reflect.ValueOf(3)).IsValid())
}
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
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
# 6、结构体反射
任意值通过
reflect.TypeOf()
获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type
)的NumField()
和Field()
方法获得结构体成员的详细信息.reflect.Type
中与获取结构体成员相关的的方法如下表所示
方法 | 说明 |
---|---|
Field(i int) StructField | 根据索引,返回索引对应的结构体字段的信息. |
NumField() int | 返回结构体成员字段数量. |
FieldByName(name string) (StructField, bool) | 根据给定字符串返回字符串对应的结构体字段的信息. |
FieldByIndex(index []int) StructField | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息. |
FieldByNameFunc(match func(string) bool) (StructField,bool) | 根据传入的匹配函数匹配需要的字段. |
NumMethod() int | 返回该类型的方法集中方法的数目 |
Method(int) Method | 返回该类型方法集中的第i个方法 |
MethodByName(string)(Method, bool) | 根据方法名返回该类型方法集中的方法 |
# 7、反射示例
package main
import (
"fmt"
"reflect"
)
type Call struct {
Num1 int
Num2 int
}
func (call Call) GetSub(name string) {
fmt.Printf("%v 完成了减法运算,%v - %v = %v \n", name, call.Num1, call.Num2, call.Num1-call.Num2)
}
func (call *Call) GetSum(name string) {
fmt.Printf("%v 完成了加法运算,%v + %v = %v \n", name, call.Num1, call.Num2, call.Num1+call.Num2)
}
func main() {
var (
call *Call
rValues []reflect.Value
rValues2 []reflect.Value
)
ptrType := reflect.TypeOf(call) //获取call的指针的reflect.Type
trueType := ptrType.Elem() //获取type的真实类型
ptrValue := reflect.New(trueType) //返回对象的指针对应的reflect.Value
call = ptrValue.Interface().(*Call)
trueValue := ptrValue.Elem() //获取真实的结构体类型
trueValue.FieldByName("Num1").SetInt(123) //设置对象属性,注意这个一定要是真实的结构类型的reflect.Value才能调用,指针类型reflect.Value的会报错
//ptrValue.FieldByName("Num2").SetInt(23)
trueValue.FieldByName("Num2").SetInt(23)
//rValues = make([]reflect.Value, 0)
rValues = append(rValues, reflect.ValueOf("xiaopeng")) //调用对应的方法
fmt.Println(rValues)
trueValue.MethodByName("GetSub").Call(rValues)
/*
fixme 在反射中,指针的方法不可以给实际类型调用,实际类型的方法可以给指针类型调用,因为go语言对这种操作做了封装
所以下面一句是没问题的
下下一句会运行时报错
*/
//ptrValue.MethodByName("GetSub").Call(rValues)
//trueValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram")))
ptrValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram")))
fmt.Println(call)
/*
fixme 在实际使用中 指针和实体都能相互转换,不会影响调用
但是指针的方法在方法体内的操作会影响到结构体本身属性
而实体的方法不会,因为go对于结构体、数组、基本类型都是值传递
*/
call.GetSub("aaa")
(*call).GetSub("bbb")
call.GetSum("ccc")
(*call).GetSum("ddd")
}
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
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
# 8、反射的应用
- 不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射.例如以下这种桥接模式:
func bridge(funcPtr interface{}, args ...interface{})
1
- 不知道传入函数的参数类型,函数需要在运行时处理任意参数对象,这种需要对结构体对象反射.典型应用场景是
ORM
,gorm
示例如下:
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // set field size to 255
MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
Num int `gorm:"AUTO_INCREMENT"` // set num to auto incrementable
Address string `gorm:"index:addr"` // create index with name `addr` for address
IgnoreMe int `gorm:"-"` // ignore this field
}
var users []User
db.Find(&users)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 9、官方文档地址
- https://golang.google.cn/pkg/reflect/
- https://golangbot.com/reflection/
上次更新: 2024/04/09, 16:48:42
- 01
- AWS NAT-NetWork-Firwalld配置(一)04-09
- 02
- AWS NAT-NetWork-Firwalld配置(二)04-09
- 03
- kubernetes部署minio对象存储01-18