go反射教程

1. 简单示例

需求:给定一个结构体,判断其字段类型并进行修改

package main

import (
	"fmt"
	"reflect"
)

type MyInt int
type User struct {
	IdOrName any   `json:"idOrName"`
	M        MyInt `json:"m"`
}

func main() {
	str := 1
	u := User{IdOrName: str, M: 20}
	valueOf := reflect.ValueOf(&u).Elem()
	elem := valueOf.FieldByName("IdOrName")
	elem2 := valueOf.FieldByName("M")
	fmt.Println(elem2.Type())
	fmt.Println(elem2.Kind())
	if elem.Kind() == reflect.Interface {
		if elem.Elem().Kind() == reflect.String {
			elem.Set(reflect.ValueOf("这是string类型,所以是名字"))
		}
		if elem.Elem().Kind() == reflect.Int ||
			elem.Elem().Kind() == reflect.Int64 ||
			elem.Elem().Kind() == reflect.Int32 {
			elem.Set(reflect.ValueOf(1000))
		}
	}
	fmt.Println(u)
	//如果想要获取字段相关的信息,使用TypeOf
	typeOf := reflect.TypeOf(u)
	for i := 0; i < typeOf.NumField(); i++ {
		field := typeOf.Field(i)
		fmt.Println("name:", field.Name)
		fmt.Println("type:", field.Type)
		fmt.Println("kind:", field.Type.Kind())
		fmt.Println("tag:", field.Tag)
	}
}

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

从上述案例中,我们可以了解一些基本的知识:

  • 获取值使用ValueOf
  • 获取元素使用Elem
  • 判断类型使用Kind或者Type,如果类型使用type定义,Kind返回的是底层类型
  • 设置值使用Set
  • 如果想修改值,必须为指针类型
  • 如果想要获取结构体信息或者类型,可以使用TypeOf

2. 反射定义

简单讲:反射就是在程序运行时,可以访问自身结构并且做出修改的一种能力(审视自身)。

在golang中,反射是通过reflect包来实现。

反射是建立在类型系统上的,以下是go中空接口interface{}的定义:

//runtime/runtime2.go
type eface struct {
	_type *_type //类型信息
	data  unsafe.Pointer//数据信息,指向数据指针
}
1
2
3
4
5

这里面包含两个重要的变量:

  • 类型

对应到反射中,有两个方法:

  • reflect.TypeOf:获取类型信息,返回Type类型
  • reflect.ValueOf() 获取数据信息,返回 Value 类型。
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i any) Value {}
// 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 any) Type {}
1
2
3
4
5
6

go反射中,都是通过将interface{}转换为Type或者Value类型,然后通过对Type和Value的操作,来实现相应的功能

3. 反射三定律

在2011年的官方一篇博客中,有描述反射的三个定律:

地址:https://go.dev/blog/laws-of-reflection

  • 第一定律:反射从接口值转变为反射对象(Reflection goes from interface value to reflection object)
  • 第二定律:反射从反射对象转变为接口值(Reflection goes from reflection object to interface value)
  • 第三定律:要修改反射对象的值,其值必须可以设置(To modify a reflection object, the value must be settable)
package main

import (
	"fmt"
	"reflect"
)

func main() {
	var x float64 = 3.14
	//第一定律
	fmt.Println("type:", reflect.TypeOf(x))
	//第二定律
	xValueOf := reflect.ValueOf(x)
	y := xValueOf.Interface().(float64)
	fmt.Println("y=", y)
	//第三定律
	//这个会报错
	//xValueOf.SetFloat(7.1)
	pointValueOf := reflect.ValueOf(&x)
	elem := pointValueOf.Elem()
	elem.SetFloat(7.1)
	fmt.Println("x=", x)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

4. Kind

在前面我们提到,Kind返回一个类型,返回的类型在reflect包有定义:

// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid 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
	Pointer
	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

5. reflect.Type

通过TypeOf获取到Type类型后,就可以获取类型相关的一些信息,比如Slice长度,struct成员信息,函数参数等

5.1 Struct

5.1.1 获取成员变量信息

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	UserName string `json:"userName"`
	Age      int    `json:"age"`
	Gender   string `json:"gender"`
}

func main() {
	var user = User{
		UserName: "mszlu",
		Age:      18,
		Gender:   "男",
	}
	typeUser := reflect.TypeOf(user)
	numField := typeUser.NumField()
	for i := 0; i < numField; i++ {
		field := typeUser.Field(i)
		fmt.Printf("%d : name(变量名称)=%s offset(首地址偏移量)=%d \n"+
			"anonymous(是否为匿名变量)=%t type(变量类型)=%s exported(是否可见)=%t tag=%s\n",
			i, field.Name,
			field.Offset,
			field.Anonymous,
			field.Type,
			field.IsExported(),
			field.Tag,
		)
	}
	//除了上述方式,也可以使用变量名称获取字段
	if un, ok := typeUser.FieldByName("UserName"); ok {
		fmt.Printf("name(变量名称)=%s offset(首地址偏移量)=%d \n"+
			"anonymous(是否为匿名变量)=%t type(变量类型)=%s exported(是否可见)=%t tag=%s\n",
			un.Name,
			un.Offset,
			un.Anonymous,
			un.Type,
			un.IsExported(),
			un.Tag,
		)
	}
	//还可以根据索引获取
	age := typeUser.FieldByIndex([]int{1})
	fmt.Printf("name(变量名称)=%s offset(首地址偏移量)=%d \n"+
		"anonymous(是否为匿名变量)=%t type(变量类型)=%s exported(是否可见)=%t tag=%s\n",
		age.Name,
		age.Offset,
		age.Anonymous,
		age.Type,
		age.IsExported(),
		age.Tag,
	)
}

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

5.1.2 获取方法信息

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	UserName string `json:"userName"`
	Age      int    `json:"age"`
	Gender   string `json:"gender"`
}

func (u User) GetName() string {
	return u.UserName
}
func (u *User) GetAge() int {
	return u.Age
}
func main() {
	var user = User{
		UserName: "mszlu",
		Age:      18,
		Gender:   "男",
	}
	//指针的方法不包含
	//typeUser := reflect.TypeOf(user)
	//methodNum := typeUser.NumMethod()
	//for i := 0; i < methodNum; i++ {
	//	method := typeUser.Method(i)
	//	fmt.Printf("method name:%s ,type:%s, exported:%t\n", method.Name, method.Type, method.IsExported())
	//}
	//指针或者值的方法都包含
	typeUserPoint := reflect.TypeOf(&user)
	methodNumPoint := typeUserPoint.NumMethod()
	for i := 0; i < methodNumPoint; i++ {
		method := typeUserPoint.Method(i)
		fmt.Printf("method name:%s ,type:%s, exported:%t\n", method.Name, method.Type, method.IsExported())
	}
}

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

5.1.3 获取函数的信息

package main

import (
	"fmt"
	"reflect"
)

type User struct {
}

func (*User) Add(a, b int) string {
	return fmt.Sprintf("%d", a+b)
}

func main() {
	u := &User{}
	typeAdd := reflect.TypeOf(u.Add)
	fmt.Printf("func kind is %s \n", typeAdd.Kind())
	in := typeAdd.NumIn()   //输入参数的个数
	out := typeAdd.NumOut() //输出参数的个数
	for i := 0; i < in; i++ {
		argType := typeAdd.In(i)
		fmt.Printf("第%d个输入参数的类型:%s \n", i, argType)
	}
	for i := 0; i < out; i++ {
		argType := typeAdd.Out(i)
		fmt.Printf("第%d个输出参数的类型:%s \n", i, argType)
	}
}

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

5.1.4 是否实现了接口

package main

import (
	"fmt"
	"reflect"
)

type People interface {
	Color() string
}
type User struct {
}

func (*User) Color() string {
	return "yellow"
}

type Dog struct {
}

func main() {
	//我们想要知道Dog和User哪个实现了People接口呢?

	//先获取接口类型 这原理是把nil强制转换为了*People
	peopleType := reflect.TypeOf((*People)(nil)).Elem()
	fmt.Println("people is interface? ", peopleType.Kind() == reflect.Interface)
	valueUserType := reflect.TypeOf(User{})
	pointUserType := reflect.TypeOf(&User{})
	valueDogType := reflect.TypeOf(Dog{})
	pointDogType := reflect.TypeOf(&Dog{})
	fmt.Println("value user 是否实现people接口:", valueUserType.Implements(peopleType))
	fmt.Println("point user 是否实现people接口:", pointUserType.Implements(peopleType))
	fmt.Println("value dog 是否实现people接口:", valueDogType.Implements(peopleType))
	fmt.Println("point dog 是否实现people接口:", pointDogType.Implements(peopleType))
}

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

6. reflect.Value

value就是用来做获取值,修改值等关于值的操作的。

比如:


type User struct {
	Name string
}

func main() {
	u := User{Name: "mszlu"}
	//现在我们想要获取Name的值 mszlu 这时候就应该使用value去获取
	valueOfUser := reflect.ValueOf(u)
	fmt.Println("Name is ", valueOfUser.FieldByName("Name"))
}

1
2
3
4
5
6
7
8
9
10
11
12

6.1 转Type

type User struct {
	Name string
}

func main() {
	u := User{Name: "mszlu"}
	//现在我们想要获取Name的值 mszlu 这时候就应该使用value去获取
	valueOfUser := reflect.ValueOf(u)
	typeOfUser := valueOfUser.Type()
	name, _ := typeOfUser.FieldByName("Name")
	fmt.Println("Name is ", valueOfUser.FieldByName("Name"))
	fmt.Println("Name type is ", name.Type)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

6.2 指针Value互转

type User struct {
	Name string
}

func main() {
	u := User{Name: "mszlu"}
	//指针value 转 非指针value
	point := reflect.ValueOf(&u)
	noPoint := point.Elem()
	fmt.Printf("point type: %s, noPoint type: %s \n", point.Type(), noPoint.Type())
	fmt.Printf("point kind: %s, noPoint kind: %s \n", point.Kind(), noPoint.Kind())
	//非指针转指针
	newPoint := noPoint.Addr()
	fmt.Printf("newPoint type: %s, newPoint kind: %s \n", newPoint.Type(), newPoint.Kind())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

6.3 获取原始类型

type User struct {
	Name string
}

func main() {
	u := User{Name: "mszlu"}
	//现在需要从value获取到user
	v := reflect.ValueOf(u)
	newUser := v.Interface().(User)
	fmt.Println(newUser.Name)
}
1
2
3
4
5
6
7
8
9
10
11

6.4 空value判断


type User struct {
	Name string
}

func main() {
	var i interface{} //接口没有指向具体的值
	v := reflect.ValueOf(i)
	fmt.Printf("v持有值 %t, type of v is Invalid %t\n", v.IsValid(), v.Kind() == reflect.Invalid)

	var user *User = nil
	v = reflect.ValueOf(user) //Value指向一个nil
	if v.IsValid() {
		fmt.Printf("v持有的值是nil %t\n", v.IsNil()) //调用IsNil()前先确保IsValid(),否则会panic
	}

	var u User //只声明,里面的值都是0值
	v = reflect.ValueOf(u)
	if v.IsValid() {
		fmt.Printf("v持有的值是对应类型的0值 %t\n", v.IsZero()) //调用IsZero()前先确保IsValid(),否则会panic
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

6.5 修改值

type User struct {
	Name string
	age  int
}

func main() {
	var user = User{
		Name: "mszlu",
	}
	//注意: 修改值 比如传指针
	valueOf := reflect.ValueOf(&user)
	valueOf.Elem().FieldByName("Name").SetString("new mszlu")
	fmt.Println(user.Name)
	ageValue := valueOf.Elem().FieldByName("age")
	if ageValue.CanSet() {
		ageValue.SetInt(18)
	} else {
		fmt.Println("私有成员不能修改值")
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

6.6 调用方法


type User struct {
	Name string
	age  int
}

func (u *User) GetName() string {
	return u.Name
}
func main() {
	var user = User{
		Name: "mszlu",
	}
	//注意:调用方法时,使用指针,可以调用指针类型的方法和非指针类型的方法
	valueOf := reflect.ValueOf(&user)
	methodByName := valueOf.MethodByName("GetName")
	//无参数传空切片
	result := methodByName.Call([]reflect.Value{})
	fmt.Println(result[0].Interface().(string))
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

7. 其他

7.1 创建

type User struct {
	Name string
	age  int
}

func (u *User) GetName() string {
	return u.Name
}
func main() {
	//创建struct
	userType := reflect.TypeOf(User{})
	valueUser := reflect.New(userType) //这里new出来的是指针 相当于reflect.ValueOf(&User{})
	valueUser.Elem().FieldByName("Name").SetString("mszlu")
	fmt.Println(valueUser)
	//创建切片
	userSliceType := reflect.TypeOf([]User{})
	userSliceValue := reflect.MakeSlice(userSliceType, 1, 3)
	userSliceValue.Index(0).Set(reflect.ValueOf(User{Name: "mszlu"}))
	users := userSliceValue.Interface().([]User)
	fmt.Println(users[0].Name)
	//其他还有MakeMap,MakeChan,MakeFunc等 可自行使用一下

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

7.2 修改slice


type User struct {
	Name string
	age  int
}

func (u *User) GetName() string {
	return u.Name
}
func main() {
	//创建切片
	users := make([]*User, 1, 3)
	users[0] = &User{
		Name: "mszlu",
	}
	//修改切片 必须使用指针
	userSliceValue := reflect.ValueOf(&users)
	//我们可以改变切片的长度
	//userSliceValue.Elem().Index(1).Set(reflect.ValueOf(&User{Name: "mszlu"})) //会报错 slice index out of range
	userSliceValue.Elem().SetLen(2)
	userSliceValue.Elem().Index(1).Set(reflect.ValueOf(&User{Name: "mszlu1"}))
	fmt.Println(users[1].Name)

	//也可以直接Append
	userSliceValue = reflect.Append(userSliceValue.Elem(), reflect.ValueOf(&User{Name: "mszlu2"}))
	users = userSliceValue.Interface().([]*User)
	fmt.Println(users[2].Name)
}

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