go专题-reflect反射

2022/7/21 go

🌙 1.反射概述

反射是指在程序运行期对程序本身进行访问和修改的能力,即可以在运行时动态获取变量的各种信息,比如变量的类型(type),值(value),如果是结构体变量,还可以获取到结构体本身的信息(字段与方法),通过反射,还可以修改变量的值,可以调用关联的方法。

每种语言的反射模型都不同,并且有些语言根本不支持反射。

Go语言实现了反射,反射机制就是在运行时动态调用对象的方法和属性,即可从运行时态的示例对象反求其编码阶段的定义。

注意:

  • 在编译期间,无法对反射代码进行一些错误提示。
  • 反射影响性能

🌙 2.反射方法

标准库中reflect包提供了相关的功能。在reflect包中,通过reflect.TypeOf()reflect.ValueOf()分别从类型的角度来描述一个Go对象。

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {...}

// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i interface{}) Value {...}
1
2
3
4
5
6
7

在Go语言的实现中,一个interface类型的变量存储了2类信息, <value,type>:

  • value 是变量的值。reflect.ValueOf获取变量的值,返回值 Value类型
  • type 是变量的类型。reflect.TypeOf获取变量的类型,返回值Type类型、即 concrete type
func testDemo1() {
	var x float32 = 3.14
	v := reflect.ValueOf(x)
	t := reflect.TypeOf(x)
	fmt.Println(v) // 返回Value类型对象,值为3.14
	fmt.Println(t) // 返回Type类型对象,值为float32
}
1
2
3
4
5
6
7

🌙 3.方法返回值类型

// Value 结构体
type Value struct {...}
// Type returns v's type.
func (v Value) Type() Type {...}
func (v Value) Kind() Kind {...}
... ...

// Type 接口
type Type interface {
	// NumMethod returns the number of methods accessible using Method.
	//
	// Note that NumMethod counts unexported methods only for interface types.
	NumMethod() int

	// Name returns the type's name within its package for a defined type.
	// For other (non-defined) types it returns the empty string.
	Name() string

	// Kind returns the specific kind of this type.
	Kind() Kind

	// Elem returns a type's element type.
	// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
	Elem() 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
type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func testDemo2() {
	var p1 = Person{"zkk", 17}
	v1 := reflect.ValueOf(p1)
	t1 := reflect.TypeOf(p1) // main.Person

	fmt.Println("**************Person**************")
	fmt.Println(v1) // {zkk 17}
	fmt.Println(t1) // main.Person

	fmt.Println("v1.Type()", v1.Type()) // v1.Type() main.Person
	fmt.Println("v1.Kind()", v1.Kind()) // v1.Kind() struct

	fmt.Println("t1.Kind()", t1.Kind()) // t1.Kind() struct
	fmt.Println("t1.Name()", t1.Name()) // t1.Name() Person
	//fmt.Println("t1.Elem()", t1.Elem()) // error panic

	fmt.Println("**************&Person**************")
	var p2 = &Person{"zkk", 18}
	v2 := reflect.ValueOf(p2)
	t2 := reflect.TypeOf(p2)
	fmt.Println(v2) // &{zkk 18}
	fmt.Println(t2) // *main.Person

	fmt.Println("v2.Type()", v2.Type()) // v2.Type() *main.Person
	fmt.Println("v2.Kind()", v2.Kind()) // v2.Kind() ptr

	fmt.Println("t2.Kind()", t2.Kind()) // t2.Kind() ptr
	fmt.Println("t2.Name()", t2.Name()) // t2.Name() '' 空字符串
	fmt.Println("t2.Elem()", t2.Elem()) // t2.Elem() main.Person
}
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

类型Type()与种类Kind()的区别:

  • reflect.ValueOf(p).Type()是原生数据类型: intstringboolfloat32,以及 type 定义的类型,对应的反射获取方法是 reflect.Type 中 的 Name()
  • reflect.TypeOf(p).Kind()是对象归属的品种:IntBoolFloat32ChanStringStructPtr(指针)、MapInterfaceFuncArraySliceUnsafe Pointer
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
)
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

🌙 4.静态类型和动态类型

  • 静态类型:变量声明时候赋予的类型
	type MyInt int	// int 是静态类型
	var i *int	// *int 是静态类型
1
2
  • 动态类型:运行时给这个变量赋值时,这个值的类型即为动态类型(为nil时没有动态类型)。

空接口是静态类型,必须是接口类型才能实现类型动态变化

	var A interface{} // 静态类型
	A = 10	 // 此时静态类型为 interface{} 动态为int
	A = "hello" // 此时静态类型为 interface{} 动态为string
1
2
3

🌙 5.反射三大定律

  • 1.反射可以将接口类型变量 转换为“反射类型对象”;
  • 2.反射可以将 “反射类型对象”转换为 接口类型变量;
  • 3.如果要修改 “反射类型对象” 其类型必须是 可写的CanSet()

🌙 5.反射的应用

反射常用在框架的开发上,一些常见的案例,如JSON序列化时候tag标签的产生,适配器函数的制作等,都需要用到反射。

反射的两个常见使用场景:

  • 不知道函数的参数类型:没有约定好参数、传入类型很多,此时类型不能统一表示,需要反射
  • 不知道调用哪个函数:比如根据用户的输入来决定调用特定函数,此时需要依据函数、函数参数进行反射,在运行期间动态执行函数

🌙 5.1操作简单数据类型

func testDemo3() {
	var n int64 = 111
	// 设置值,指针传递
        // 定律1:反射可以将接口类型变量 转换为“反射类型对象”
	ptrValue := reflect.ValueOf(&n)
	// Elem()用于获取原始值的反射对象
	newValue := ptrValue.Elem()
	fmt.Println("type:", newValue.Type()) // type: int64
        // 是否可写can set
        // 定律3:当反射对象所存的值是可设置时,反射对象才可修改
	fmt.Println("can set:", newValue.CanSet()) // can set: true
	newValue.SetInt(666) // 修改为666

	// 获取值,值传递
	nv := reflect.ValueOf(n)
	fmt.Println(nv.Int()) // 666
        // 定律2:反射可以将 “反射类型对象”转换为 接口类型变量;
	fmt.Println(nv.Interface().(int64)) // 666
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

🌙 5.2进行类型推断

func checkType(i interface{}) {
	t := reflect.TypeOf(i)

	switch t.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		fmt.Println("Int number")
	case reflect.Float32, reflect.Float64:
		fmt.Println("Float number")
	case reflect.String:
		fmt.Println("String")
	case reflect.Slice:
		fmt.Println("Slice")
	case reflect.Map:
		fmt.Println("Map")
	case reflect.Struct:
		fmt.Println("Struct")
	default:
		fmt.Printf("Unknown %v\n", t)
	}
}

func getInfo(i interface{}) {
	t := reflect.TypeOf(i)
	fmt.Printf("%v\n", t)

	v := reflect.ValueOf(i)
	fmt.Printf("%v\n", v)

	if k := t.Kind(); k != reflect.Struct {
		fmt.Println("Input Type is not Struct")
		return
	}

	for j := 0; j < t.NumField(); j++ {
		f := t.Field(j)
		fv := v.Field(j).Interface()
		fmt.Printf("%8s %v=%v\n", f.Name, f.Type, fv)
	}
}

type User struct {
	Name   string
	Age    int
	Gender string
}

func (u *User) Greet(msg string) {
	fmt.Printf("My name is %s, age %d, msg:%v\n", u.Name, u.Age, msg)
}
func testDemo6() {
	u := User{Age: 18, Name: "JEEK", Gender: "female"}
	checkType(u) // Struct
	checkType(&u) // Unknown *main.User
        getInfo(u)
/* main.User
{JEEK 18 female}
    Name string=JEEK
     Age int=18
  Gender string=man
*/
}
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

🌙 5.3创建slice、map、chan

func testDemo5() {
	// 反射创建map slice channel
	intSlice := make([]int, 0)
	mapStringInt := make(map[string]int)
	sliceType := reflect.TypeOf(intSlice)
	mapType := reflect.TypeOf(mapStringInt)

	// 创建新值
	intSliceReflect := reflect.MakeSlice(sliceType, 0, 0)
	mapReflect := reflect.MakeMap(mapType)

	// 使用新创建的变量
	n := 11
	nv := reflect.ValueOf(n)
	intSliceReflect = reflect.Append(intSliceReflect, nv)
	intSlice1 := intSliceReflect.Interface().([]int)
	fmt.Println(intSlice1) // [11]

	k := "hello"
	kv := reflect.ValueOf(k)
	mapReflect.SetMapIndex(kv, nv)
	mapStringInt1 := mapReflect.Interface().(map[string]int)
	fmt.Println(mapStringInt1) // map[hello:11]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

🌙 5.4创建函数

func createTimedFunc(i interface{}) interface{} {
	rt := reflect.TypeOf(i)
	if rt.Kind() != reflect.Func {
		panic("not Reflect.Func")
	}
	rv := reflect.ValueOf(i)
	wf := reflect.MakeFunc(rt, func(in []reflect.Value) []reflect.Value {
		st := time.Now()
		rt := rv.Call(in)
		et := time.Now()
		fmt.Printf("execute %s took %v\n", runtime.FuncForPC(rv.Pointer()).Name(), et.Sub(st))
		return rt
	})
	return wf.Interface()
}

func test1() {
	fmt.Println("test1 start")
	time.Sleep(time.Second * 2)
	fmt.Println("test1 end")
}

func test2(n int) int {
	fmt.Println("test2 start")
	time.Sleep(time.Duration(n) * time.Second)
	fmt.Println("test2 end")
	return n
}

func testDemo7() {
	timedTest1 := createTimedFunc(test1).(func())
	timedTest1()

	timedTest2 := createTimedFunc(test2).(func(int) int)
	fmt.Println(timedTest2(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

结果:

test1 start
test1 end
execute main.test1 took 2.0100738s
test2 start
test2 end
execute main.test2 took 4.0011527s
4
1
2
3
4
5
6
7

reflect.Value.Call (var) 源码:

// Call calls the function v with the input arguments in.
// For example, if len(in) == 3, v.Call(in) represents the Go call v(in[0], in[1], in[2]).
// Call panics if v's Kind is not Func.
// It returns the output results as Values.
// As in Go, each input argument must be assignable to the
// type of the function's corresponding input parameter.
// If v is a variadic function, Call creates the variadic slice parameter
// itself, copying in the corresponding values.
func (v Value) Call(in []Value) []Value {
	v.mustBe(Func)
	v.mustBeExported()
	return v.call("Call", in)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

🌙 5.5操作结构体

type User struct {
	Name   string `json:"name" tag:"姓名"`
	Age    int    `json:"age" tag:"年龄"`
	Gender string `json:"gender" tag:"性别"`
}

func (u *User) Greet(msg string) {
	fmt.Printf("My name is %s, age %d, msg:%v\n", u.Name, u.Age, msg)
}
1
2
3
4
5
6
7
8
9

下面通过reflect来操作结构体User

  • 字段读取
func testDemo8() {
	u := User{Age: 18, Name: "JEEK", Gender: "Female"}
	t := reflect.TypeOf(u)
	v := reflect.ValueOf(u)

	fmt.Printf("%v\n", t.Field(0)) // {Name  string  0 [0] false}
	fmt.Printf("%v\n", v.FieldByName("Gender")) // Female

        // 读取tag
	f := t.Field(1) // Age
	fmt.Printf("f.tag: %s\n", f.Tag) // json:"age" tag:"年龄"
	fmt.Printf("f.tag get: %s\n", f.Tag.Get("json")) // age
	tag, _ := f.Tag.Lookup("tag")
	fmt.Printf("f.tag Lookup: %s\n", tag) // 年龄
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 方法调用
func testDemo9() {
	u := &User{Age: 18, Name: "JEEK", Gender: "Female"}
	v := reflect.ValueOf(u)

	m := v.MethodByName("Greet")
	args := []reflect.Value{reflect.ValueOf("Hello world!")}
	m.Call(args) // My name is JEEK, age 18, msg:Hello world!
}
1
2
3
4
5
6
7
8

🌙 5.6综合案例

type ss struct {
	int
	string
	bool
	float64
}

func (s ss) Method1(i int) string {
	return "结构体方法1:值接收器"
}

func (s *ss) Method2(i int) string {
	return "结构体方法2:指针接收器"
}

var structValue = ss{
	20,
	"结构体",
	true,
	3.14,
}
var complexTypes = []interface{}{
	structValue,
	&structValue,
}

func printInfo(i interface{}) {
	if i == nil {
		fmt.Printf("----------------------------")
		fmt.Printf("无效的接口值:%v\n", i)
		fmt.Println("----------------------------")
		return
	}
	fmt.Printf("\n---------------【%#v】-----------------\n", i)
	v := reflect.ValueOf(i)
	printValue(v)
}

func printValue(v reflect.Value) {
	fmt.Println("反射值字符串[reflect.ValueOf(i).String()]      :", v.String())
	fmt.Println("反射值类型[reflect.ValueOf(i).Type()]         :", v.Type())
	fmt.Println("反射值类别[reflect.ValueOf(i).Kind()]         :", v.Kind())
	fmt.Println("是否可取址[reflect.ValueOf(i).CanAddr()]      :", v.CanAddr())
	fmt.Println("是否可修改[reflect.ValueOf(i).CanSet()]       :", v.CanSet())
	if v.CanAddr() {
		fmt.Println("获取地址[reflect.ValueOf(i).Addr()]		   :", v.Addr())
		fmt.Println("获取自由地址[reflect.ValueOf(i).UnsafeAddr()]  :", v.UnsafeAddr())
	}
	fmt.Println("获取方法数量[reflect.ValueOf(i).NumMethod()]  :", v.NumMethod())
	if v.NumMethod() > 0 {
		i := 0
		for ; i < v.NumMethod()-1; i++ {
			fmt.Printf("		├ %v\n", v.Method(i).String())
		}
		fmt.Printf("		└ %v\n", v.Method(i).String())
	}
	fmt.Println("通过名称获取方法1[(reflect.ValueOf(i).MethodByName(Method1))]       :", v.MethodByName("Method1").String())
	fmt.Println("通过名称获取方法2[(reflect.ValueOf(i).MethodByName(Method2))]       :", v.MethodByName("Method2").String())

	switch v.Kind() {
	case reflect.Struct:
		fmt.Println("==========结构体==========")
		fmt.Println("结构体字段个数[(reflect.ValueOf(i).NumField())]:", v.NumField())
		if v.NumField() > 0 {
			var i int
			for i = 0; i < v.NumField()-1; i++ {
				field := v.Field(i)
				fmt.Printf("    ├ %-8v %v\n", field.Type(), field.String())
			}
			field := v.Field(i)
			fmt.Printf("    └ %-8v %v\n", field.Type(), field.String())
		}
	}
}

func testDemo10() {
	for i := 0; i < len(complexTypes); i++ {
		printInfo(complexTypes[i])
	}
}
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

结果:

---------------【main.ss{int:20, string:"结构体", bool:true, float64:3.14}】-----------------
反射值字符串[reflect.ValueOf(i).String()]      : <main.ss Value>
反射值类型[reflect.ValueOf(i).Type()]         : main.ss
反射值类别[reflect.ValueOf(i).Kind()]         : struct
是否可取址[reflect.ValueOf(i).CanAddr()]      : false
是否可修改[reflect.ValueOf(i).CanSet()]       : false
获取方法数量[reflect.ValueOf(i).NumMethod()]  : 1
		└ <func(int) string Value>
通过名称获取方法1[(reflect.ValueOf(i).MethodByName(Method1))]       : <func(int) string Value>
通过名称获取方法2[(reflect.ValueOf(i).MethodByName(Method2))]       : <invalid Value>
==========结构体==========
结构体字段个数[(reflect.ValueOf(i).NumField())]: 4
    ├ int      <int Value>
    ├ string   结构体
    ├ bool     <bool Value>
    └ float64  <float64 Value>

---------------【&main.ss{int:20, string:"结构体", bool:true, float64:3.14}】-----------------
反射值字符串[reflect.ValueOf(i).String()]      : <*main.ss Value>
反射值类型[reflect.ValueOf(i).Type()]         : *main.ss
反射值类别[reflect.ValueOf(i).Kind()]         : ptr
是否可取址[reflect.ValueOf(i).CanAddr()]      : false
是否可修改[reflect.ValueOf(i).CanSet()]       : false
获取方法数量[reflect.ValueOf(i).NumMethod()]  : 2
		├ <func(int) string Value>
		└ <func(int) string Value>
通过名称获取方法1[(reflect.ValueOf(i).MethodByName(Method1))]       : <func(int) string Value>
通过名称获取方法2[(reflect.ValueOf(i).MethodByName(Method2))]       : <func(int) string 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

可以发现structValue,&structValue的反射结果是不一样的,指针对象在这里有两个方法,而值对象只有一个方法,这是因为Method2()方法是指针方法,在值对象中是不能被反射到的