reflect 简介

reflect 是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为

反射作为一种元编程方式可以减少重复代码,但使用不当,也会使程序变得过于抽象,难以理解,且运行缓慢。

反射机制:在编译时不知道类型的情况下,可更新变量、运行时查看值调用方法以及直接对他们的布局进行操作。反射机制就是在运行时动态的调用对象的方法和属性。

反射和自省的区别

“反射” 和 “内省”(type introspection)在概念上有区别。内省(或称 “自省”)机制仅指程序在运行时对自身信息(称为元数据)的检测;反射机制不仅包括要能在运行时对程序自身信息进行检测,还要求程序能进一步根据这些信息改变程序状态或结构。所以反射的概念范畴要大于内省。

什么时候需要反射

有时候我们需要一个函数可以处理各种类型的值。在不知道类型的情况下,你可能会这么写:

1
2
3
4
5
6
7
8
9
// 伪代码
switch value := value.(type) {
case string:
// ...一些操作
case int:
// ...一些操作
case cbsStruct: // 自定义的结构体
// ...一些操作
}

有没发现什么问题?当类型很多,这个函数会写的非常长,而且还可能存在自定的类型,也就是说这个判断日后可能还要一直改,因为无法知道未知值到底属于什么类型。在无法透视一个未知类型的时候,以上代码其实不是很合理,这时候就需要有反射来帮忙你处理,

什么时候需要反射:

  • 不能预先确定参数的类型,可能是因为没有约定好,也可能是传入的参数类型有很多,且不能统一表示。
  • 函数需要根据输入的参数来动态的执行不同的行为。

反射的优缺点

优点:

  • 可以在一定程度上避免硬编码,提供灵活性和通用性。
  • 可以作为一个第一类对象发现并修改源代码的结构(如代码块、类、方法、协议等)。

缺点:

  • 同样因为反射的概念和语法都比较抽象,滥用反射会使得代码难以被其他人读懂。
  • 无法在编译时检查错误。代码在运行时存在 panic 的风险。
  • 降低了代码运行的效率,反射出变量的类型需要额外的开销。

使用 python 模拟 reflect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 使用动态方法映射
class ReflectMethodFactory(object):
def __init__(self, action):
self.action = action
self.reflect = {
"get": self.get,
"post": self.post
}

def get(self):
print("get")

def post(self):
print("post")


def main():
r = ReflectMethodFactory("get")
r.reflect[r.action]()


if __name__ == "__main__":
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 未使用动态方法映射
class ReflectMethodFactory(object):
def __init__(self, action):
self.action = action

def get(self):
print("get")

def post(self):
print("post")


def main():
r = ReflectMethodFactory("get")

if r.action == "get":
r.get()
elif r.action == "post":
r.post()

# ..... else else

如果不实用动态映射的话, 来一个方法我就需要加一个if, 我事先定义好一组规则, 然后进行动态映射, 这样是不是方便了很多? 本身是弱类型语言所以可以这么搞, 但是golang中就不能这么搞, 所以才有了reflect。

深入理解反射

变量的 static type 和 concrete type

变量包括(type, value)两部分。type 包括 static type 和 concrete type。

  • static type 是你在编码是看见的类型 (如 int、string),static type 在创建变量的时候就已经确定。
  • concrete type 是 runtime 系统看见的类型。
1
2
3
4
5
graph TD;
Var-->type;
Var-->value;
type-->staticType;
type-->concreteType;

eg:

1
2
3
4
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)

var r io.Reader
r = tty

此时,对于变量 r:

  • value 为 tty
  • static type 为 io.Reader
  • concrete type 为 *os.File

对于interface{}变量,其 static type 就是 interface{}:

1
2
3
4
tty2, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)

var r2 interface{}
r2 = tty2

此时,对于变量 r2:

  • value 为 tty2
  • static type 为 interface{}
  • concrete type 为 *os.File

类型断言的本质

类型断言能否成功,取决于变量的 concrete type,而不是 static type。因此,一个 reader 变量如果它的 concrete type 也实现了 write 方法的话,它也可以被类型断言为 writer。

1
2
var w io.Writer
w = r.(io.Writer)

interface{} 类型的两个指针

众所周知,反射是针对 interface 类型的,只有 interface 类型才有反射一说。而所有的 interface 类型,其 static type 都为 interface{}

因此,每个 interface 变量都有一个对应 pair:

1
(value, type)

pair 中 value 记录了实际变量的值,type 记录了实际变量的类型。

也就是说,一个 interface{} 类型的变量包含了 2 个指针:

  • 一个指针指向实际的值【对应 value】
  • 一个指针指向值的类型【对应 concrete type】

反射的本质

interface 及其 pair 的存在,是 Golang 中实现反射的前提。反射就是用来检测存储在接口变量内部 (值 value;类型 concrete type) pair 对的一种机制

TypeOf()、ValueOf() 和 pair 的关系

concrete tpye 决定了该变量支持哪些方法集。value 决定了该变量在内存值的读写。

为了修改 value 和 concrete type,reflect 提供了 TypeOf 和 ValueOf 两个方法:

  • func ValueOf(i interface{}) Value {...}:获取 pair 中的 type。
  • func TypeOf(i interface{}) Type {...} :获取 pair 中的 value。

反射三大定律

  • 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.

Reflection goes from interface value to reflection object.

接口值反射到反射对象

从 interface{} 变量可以反射出 reflect 对象,具体来说,通过 reflect.TypeOf 获取了变量的类型,reflect.ValueOf 获取了变量的值。

如果我们知道了一个变量的类型和值,那么就意味着我们知道了这个变量的全部信息。通过 type、value 提供的方法,可以获取变量实现的方法、类型的全部字段等等。

img

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
package reflect_test

import (
"fmt"
"reflect"
"testing"
)

type Coder struct {
Name string
}

func (c *Coder) String() string {
return c.Name
}

func TestCoder(t *testing.T) {
coder := &Coder{Name: "obgnail"}
typ := reflect.TypeOf(coder)
val := reflect.ValueOf(coder)

typeOfStringer := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
fmt.Println("kind of coder:", typ.Kind()) // kind of coder: ptr
fmt.Println("type of coder:", typ) // type of coder: *reflect_test.Coder
fmt.Println("value of coder:", val) // value of coder: obgnail
fmt.Println("implements stringer:", typ.Implements(typeOfStringer)) // implements stringer: true
}

(*fmt.Stringer)(nil) 怎么理解?

答:(*T)(nil)其实是类型转换。正常的类型转换是T(expr),如string(a),但是取址符号*的优先级更高,因此*T(expr)需要写成(*T)(expr)所以(*T)(nil)就很好理解了,这里就是将 nil 转换为*T类型。所以,var a *fmt.Stringer = (*fmt.Stringer)(nil)就是返回一个实现了*fmt.Stringer接口的变量 a。

Reflection goes from reflection object to interface value.

反射对象 变回 interface{}对象

  • 具体来说,通过 Interface()方法 从反射对象获取interface{} 变量

img

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
package reflect_test

import (
"fmt"
"reflect"
"testing"
)

type Coder struct {
Name string
}

func (c *Coder) String() string {
return c.Name
}

func TestCoder(t *testing.T) {
coder := &Coder{Name: "obgnail"}
val := reflect.ValueOf(coder)
c, ok := val.Interface().(*Coder)
if ok {
fmt.Println(c.Name)
} else {
panic("type assert to *Coder err")
}
}

可见,第一第二定律是互逆的。通过 ValueOf 方法可以从 Interface 获取反射对象,反射对象调用 Interface() 可以获取 Interface 变量。Interface 变量可以用类型转换再得到原来的变量。

img

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

要更新一个 reflect.Value,那么它的值必须是可被更新的。否则将会导致 panic。

辅助判断:

  • reflect.flag.mustBeAssignable 检查是否可以被设置
  • reflect.flag.mustBeExported 检查变量是否可以被导出,防止不可导出的的变量泄露
  • reflect.Value.assignTo 会返回一个新的反射对象,这个返回的反射对象指针会直接覆盖原反射变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// panic
func TestReflectionLaw3V1(t *testing.T) {
i := 1
v := reflect.ValueOf(i)
v.SetInt(10)
fmt.Println(i)
}

// 通过获取变量的指针来更新值
// 相当于:
// i := 1
// v := &i
// *v = 10
func TestReflectionLaw3V2(t *testing.T) {
i := 1
v := reflect.ValueOf(&i)
v.Elem().SetInt(10)
fmt.Println(i)
}

Go Reflection Rule

img

1

Elem() VS Indirect()

1
2
3
4
5
6
7
8
9
10
// Elem 返回接口 v 包含的值或指针 v 指向的值。如果 Kind(v) 不是接口或 Ptr,它就会 panic。如果 v 为 nil,则返回零值。
func (v Value) Elem() Value

// Indirect 返回 v 指向的值。如果 v 是 nil 指针,则返回零值。如果 v 不是指针,则返回 v。
func Indirect(v Value) Value {
if v.Kind() != Ptr {
return v
}
return v.Elem()
}

If a reflect.Value is a pointer, then v.Elem() is equivalent to reflect.Indirect(v). If it is not a pointer, then they are not equivalent:

  • If the value is an interface then reflect.Indirect(v) will return the same value, while v.Elem() will return the contained dynamic value.
  • If the value is something else, then v.Elem() will panic.

The reflect.Indirect helper is intended for cases where you want to accept either a particular type, or a pointer to that type. One example is the database/sql conversion routines: by using reflect.Indirect, it can use the same code paths to handle the various types and pointers to those types.

Kind()

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
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
)

反射源码分析

reflect 包的核心就在 type.go 和 value.go

  • deep_equal.go 介绍了 deep equal 的概念和实现
  • make_func.go 则是在运行时修改函数的栈信息来实现类似多态的特性
  • swapper.go 则定义了一个通用的 swapper 方法,sort 包中的 swapper 就是用的这个

reflect.go 源码结构

img

TypeOf

interface{} 类型在语言内部是通过 reflect.emptyInterface 结体表示的,其中的 rtype 字段用于表示变量的类型,另一个 word 字段指向内部封装的数据

1
2
3
4
5
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}

用于获取变量类型的 reflect.TypeOf 函数将传入的变量隐式转换成 reflect.emptyInterface 类型并获取其中存储的类型信息 reflect.rtype

reflect.TypeOf 的实现原理其实并不复杂,它只是将一个 interface{} 变量转换成了内部的 reflect.emptyInterface 表示,然后从中获取相应的类型信息

value.go 源码结构

img

ValueOf

reflect.ValueOf 函数中先调用了 reflect.escapes 保证当前值逃逸到堆上,然后通过 reflect.unpackEface 从接口中获取 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
// 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 {
if i == nil {
return Value{}
}
escapes(i)
return unpackEface(i)
}

// unpackEface converts the empty interface i to a Value.
func unpackEface(i interface{}) Value {
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}
}

Quick start

reflect 的基本功能 TypeOf 和 ValueOf

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

import (
"fmt"
"reflect"
)

func main() {
var num float64 = 1.2345

fmt.Println("type: ", reflect.TypeOf(num)) // type: float64
fmt.Println("value: ", reflect.ValueOf(num)) // value: 1.2345
}
  1. reflect.TypeOf: 直接给到了我们想要的 type 类型,如 float64、int、各种 pointer、struct 等等真实的类型
  2. reflect.ValueOf:直接给到了我们想要的具体的值,如 1.2345 这个具体数值,或者类似 &{1 “Allen.Wu” 25} 这样的结构体 struct 的值
  3. 也就是说明反射可以将 “接口类型变量” 转换为 “反射类型对象”,反射类型指的是 reflect.Type 和 reflect.Value 这两种

从 relfect.Value 中获取接口 interface 的信息

已知类型后转换为其对应的类型的做法如下,直接通过 Interface 方法然后强制转换,如下:

1
realValue := value.Interface().(已知的类型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"reflect"
)

func main() {
var num float64 = 1.2345

pointer := reflect.ValueOf(&num)
value := reflect.ValueOf(num)

// 可以理解为“强制转换”,但是需要注意的时候,转换的时候,如果转换的类型不完全符合,则直接panic
// Golang 对类型要求非常严格,类型一定要完全符合
// 如下两个,一个是*float64,一个是float64,如果弄混,则会panic
convertPointer := pointer.Interface().(*float64)
convertValue := value.Interface().(float64)

fmt.Println(convertPointer) // 0xc42000e238
fmt.Println(convertValue) // 1.2345
}
  1. 转换的时候,如果转换的类型不完全符合,则直接 panic,类型要求非常严格!
  2. 转换的时候,要区分是指针还是指
  3. 也就是说反射可以将 “反射类型对象” 再重新转换为 “接口类型变量”

未知原有类型【遍历探测其 Filed】

很多情况下,我们可能并不知道其具体类型,那么这个时候,该如何做呢?需要我们进行遍历探测其 Filed 来得知

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
package main

import (
"fmt"
"reflect"
)

type User struct {
Id int
Name string
Age int
}

func (u User) ReflectCallFunc() {
fmt.Println("Allen.Wu ReflectCallFunc")
}

func main() {

user := User{1, "Allen.Wu", 25}

DoFiledAndMethod(user)

}

// 通过接口来获取任意参数,然后一一揭晓
func DoFiledAndMethod(input interface{}) {

getType := reflect.TypeOf(input)
fmt.Println("get Type is :", getType.Name())

getValue := reflect.ValueOf(input)
fmt.Println("get all Fields is:", getValue)

// 获取方法字段
// 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
// 2. 再通过reflect.Type的Field获取其Field
// 3. 最后通过Field的Interface()得到对应的value
for i := 0; i < getType.NumField(); i++ {
field := getType.Field(i)
value := getValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}

// 获取方法
// 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
for i := 0; i < getType.NumMethod(); i++ {
m := getType.Method(i)
fmt.Printf("%s: %v\n", m.Name, m.Type)
}
}

/*
运行结果:
get Type is : User
get all Fields is: {1 Allen.Wu 25}
Id: int = 1
Name: string = Allen.Wu
Age: int = 25
ReflectCallFunc: func(main.User)
*/

通过运行结果可以得知获取未知类型的 interface 的具体变量及其类型的步骤为:

  1. 先获取 interface 的 reflect.Type,然后通过 NumField 进行遍历
  2. 再通过 reflect.Type 的 Field 获取其 Field
  3. 最后通过 Field 的 Interface () 得到对应的 value

通过运行结果可以得知获取未知类型的 interface 的所属方法(函数)的步骤为:

  1. 先获取 interface 的 reflect.Type,然后通过 NumMethod 进行遍历
  2. 再分别通过 reflect.Type 的 Method 获取对应的真实的方法(函数)
  3. 最后对结果取其 Name 和 Type 得知具体的方法名
  4. 也就是说反射可以将 “反射类型对象” 再重新转换为 “接口类型变量”
  5. struct 或者 struct 的嵌套都是一样的判断处理方式

通过 reflect.Value 设置实际变量的值

reflect.Value 是通过 reflect.ValueOf (X) 获得的,只有当 X 是指针的时候,才可以通过 reflec.Value 修改实际变量 X 的值,即:要修改反射类型的对象就一定要保证其值是 “addressable” 的。

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
package main

import (
"fmt"
"reflect"
)

func main() {

var num float64 = 1.2345
fmt.Println("old value of pointer:", num)

// 通过reflect.ValueOf获取num中的reflect.Value,注意,参数必须是指针才能修改其值
pointer := reflect.ValueOf(&num)
newValue := pointer.Elem()

fmt.Println("type of pointer:", newValue.Type())
fmt.Println("settability of pointer:", newValue.CanSet())

// 重新赋值
newValue.SetFloat(77)
fmt.Println("new value of pointer:", num)

////////////////////
// 如果reflect.ValueOf的参数不是指针,会如何?
pointer = reflect.ValueOf(num)
//newValue = pointer.Elem() // 如果非指针,这里直接panic,“panic: reflect: call of reflect.Value.Elem on float64 Value”
}

// 运行结果:
// old value of pointer: 1.2345
// type of pointer: float64
// settability of pointer: true
// new value of pointer: 77
  1. 需要传入的参数是 * float64 这个指针,然后可以通过 pointer.Elem() 去获取所指向的 Value,注意一定要是指针

  2. 如果传入的参数不是指针,而是变量,那么

    • 通过 Elem 获取原始值对应的对象则直接 panic
    • 通过 CanSet 方法查询是否可以设置返回 false
  3. newValue.CantSet () 表示是否可以重新设置其值,如果输出的是 true 则可修改,否则不能修改,修改完之后再进行打印发现真的已经修改了。

  4. reflect.Value.Elem () 表示获取原始值对应的反射对象,只有原始对象才能修改,当前反射对象是不能修改的

  5. 也就是说如果要修改反射类型对象,其值必须是 “addressable”【对应的要传入的是指针,同时要通过 Elem 方法获取原始值对应的反射对象】

  6. struct 或者 struct 的嵌套都是一样的判断处理方式

通过 reflect.ValueOf 来进行方法的调用

前面我们只说到对类型、变量的几种反射的用法,包括如何获取其值、其类型、如果重新设置新值。但是在工程应用中,另外一个常用并且属于高级的用法,就是通过 reflect 来进行方法【函数】的调用。比如我们要做框架工程的时候,需要可以随意扩展方法,或者说用户可以自定义方法,那么我们通过什么手段来扩展让用户能够自定义呢?关键点在于用户的自定义方法是未可知的,因此我们可以通过 reflect 来搞定

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
package main

import (
"fmt"
"reflect"
)

type User struct {
Id int
Name string
Age int
}

func (u User) ReflectCallFuncHasArgs(name string, age int) {
fmt.Println("ReflectCallFuncHasArgs name: ", name, ", age:", age, "and origal User.Name:", u.Name)
}

func (u User) ReflectCallFuncNoArgs() {
fmt.Println("ReflectCallFuncNoArgs")
}

// 如何通过反射来进行方法的调用?
// 本来可以用u.ReflectCallFuncXXX直接调用的,但是如果要通过反射,那么首先要将方法注册,也就是MethodByName,然后通过反射调动mv.Call

func main() {
user := User{1, "Allen.Wu", 25}

// 1. 要通过反射来调用起对应的方法,必须要先通过reflect.ValueOf(interface)来获取到reflect.Value,得到“反射类型对象”后才能做下一步处理
getValue := reflect.ValueOf(user)

// 一定要指定参数为正确的方法名
// 2. 先看看带有参数的调用方法
methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
args := []reflect.Value{reflect.ValueOf("wudebao"), reflect.ValueOf(30)}
methodValue.Call(args)

// 一定要指定参数为正确的方法名
// 3. 再看看无参数的调用方法
methodValue = getValue.MethodByName("ReflectCallFuncNoArgs")
args = make([]reflect.Value, 0)
methodValue.Call(args)
}

/*
运行结果:
ReflectCallFuncHasArgs name: wudebao , age: 30 and origal User.Name: Allen.Wu
ReflectCallFuncNoArgs
*/
  1. 要通过反射来调用起对应的方法,必须要先通过 reflect.ValueOf (interface) 来获取到 reflect.Value,得到 “反射类型对象” 后才能做下一步处理
  2. reflect.Value.MethodByName 这.MethodByName,需要指定准确真实的方法名字,如果错误将直接 panic,MethodByName 返回一个函数值对应的 reflect.Value 方法的名字。
  3. []reflect.Value,这个是最终需要调用的方法的参数,可以没有或者一个或者多个,根据实际参数来定。
  4. reflect.Value 的 Call 这个方法,这个方法将最终调用真实的方法,参数务必保持一致,如果 reflect.Value’Kind 不是一个方法,那么将直接 panic。
  5. 本来可以用 u.ReflectCallFuncXXX 直接调用的,但是如果要通过反射,那么首先要将方法注册,也就是 MethodByName,然后通过反射调用 methodValue.Call。

Demo

Call method

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
package reflect_test

import (
"fmt"
"reflect"
"testing"
)

type Spider interface {
Crawl() string
}

type ASpider struct{}
func (s *ASpider) Crawl() string { return "A" }

func RunCrawl(spider Spider) string {
reflectValue := reflect.ValueOf(spider)
method := reflectValue.MethodByName("Crawl")
value := method.Call(nil)
ret := value[0].Interface().(string)
return ret
}

func TestReflect(t *testing.T) {
a := &ASpider{}
ret := RunCrawl(a)
fmt.Println(ret)
}
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
type ReflectFactory struct {
Name string
Age int
}

func (s *ReflectFactory) A(action string) string {
return action
}

func (s *ReflectFactory) B(action string) string {
return action
}

func main() {
r := ReflectFactory{Name:"Ellison"}

// 根据传入的字符串进行动态引用结构体中的字段。
field := "Name"
res := reflect.ValueOf(&r).Elem().FieldByName(field).String()
fmt.Println(res)

// 根据传入的字符串进行动引用结构体中的方法并执行。
field2 := "B"
valueS := reflect.ValueOf(&r)
method := valueS.MethodByName(field2)
params := []reflect.Value{
reflect.ValueOf(field), // 方法参数
}
res2 := method.Call(params)[0].String() // 返回的是字符串
fmt.Println(res2)
}

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
43
44
45
46
47
48
package main

import (
"fmt"
"reflect"
)

type Student struct {
Id int
Name string
}

func (s Student) Hello(){
fmt.Println("我是一个学生")
}

func main() {
s := Student{Id: 1, Name: "咖啡色的羊驼"}

// 获取目标对象
t := reflect.TypeOf(s)
// .Name()可以获取去这个类型的名称
fmt.Println("这个类型的名称是:", t.Name()) // 这个类型的名称是: Student

// 获取目标对象的值类型
v := reflect.ValueOf(s)
// .NumField()来获取其包含的字段的总数
for i := 0; i < t.NumField(); i++ {
// 从0开始获取Student所包含的key
key := t.Field(i)

// 通过interface方法来获取key所对应的值
value := v.Field(i).Interface()

fmt.Printf("第%d个字段是:%s:%v = %v \n", i+1, key.Name, key.Type, value)
}

// 通过.NumMethod()来获取Student里头的方法
for i:=0;i<t.NumMethod(); i++ {
m := t.Method(i)
fmt.Printf("第%d个方法是:%s:%v\n", i+1, m.Name, m.Type)
}
}

// 这个类型的名称是: Student
// 第1个字段是:Id:int = 1
// 第2个字段是:Name:string = 咖啡色的羊驼
// 第1个方法是:Hello:func(main.Student)

嵌入字段的反射

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
package main

import (
"reflect"
"fmt"
)

type Student struct {
Id int
Name string
}

type People struct {
Student // 嵌入字段
}

func main() {
p := People{Student{Id: 1, Name: "咖啡色的羊驼"}}

t := reflect.TypeOf(p)
// 这里需要加一个#号,可以把struct的详情都给打印出来
// 会发现有Anonymous:true,说明是匿名字段
fmt.Printf("%#v\n", t.Field(0)) // reflect.StructField{Name:"Student", PkgPath:"", Type:(*reflect.rtype)(0x10aade0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}

// 取出这个学生的名字的详情打印出来
fmt.Printf("%#v\n", t.FieldByIndex([]int{0, 1})) // reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x109f4e0), Tag:"", Offset:0x8, Index:[]int{1}, Anonymous:false}

// 获取匿名字段的值的详情
v := reflect.ValueOf(p)
fmt.Printf("%#v\n", v.Field(0)) // main.Student{Id:1, Name:"咖啡色的羊驼"}
}

Elem()

Go语言程序中对指针获取反射对象时,可以通过 reflect.Elem() 方法获取这个指针指向的元素类型。这个获取过程被称为取元素,等效于对指针类型变量做了一个*操作

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
package main

import (
"fmt"
"reflect"
)

func main() {
// 声明一个空结构体
type cat struct {}

// 创建cat的实例
ins := &cat{}

// 获取结构体实例的反射类型对象
typeOfCat := reflect.TypeOf(ins)

// 显示反射类型对象的名称和种类
fmt.Printf("name:'%v' kind:'%v'\n",typeOfCat.Name(), typeOfCat.Kind())

// 取类型的元素
typeOfCat = typeOfCat.Elem()

// 显示反射类型对象的名称和种类
fmt.Printf("element name: '%v', element kind: '%v'\n", typeOfCat.Name(), typeOfCat.Kind())
}

// name: '' kind: 'ptr'
// element name: 'cat', element kind: 'struct'

代码说明如下:

  • 第 13 行,创建了cat结构体的实例,ins 是一个 *cat 类型的指针变量。
  • 第 16 行,对指针变量获取反射类型信息。
  • 第 19 行,输出指针变量的类型名称和种类。Go 语言的反射中对所有指针变量的种类都是 Ptr,但注意,指针变量的类型名称是空,不是 *cat。
  • 第 22 行,取指针类型的元素类型,也就是 cat 类型。这个操作不可逆,不可以通过一个非指针类型获取它的指针类型。
  • 第 25 行,输出指针变量指向元素的类型名称和种类,得到了 cat 的类型名称(cat)和种类(struct)。

reset struct field 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
package main

import (
"fmt"
"reflect"
)

type Person struct {
Name string
Age string
}
//var a string = "zhao"
//arr := reflect.ValueOf(&a)
//arr.Elem().SetString("ok")
//fmt.Println(a)
func main() {
var per Person
val := reflect.ValueOf(&per)
//val2 := reflect.ValueOf(&per)
tp := reflect.TypeOf(per)

for i:=0;i<val.Elem().NumField();i++{
if tp.Field(i).Name == "Name"{
val.Elem().Field(i).SetString("zhao")
}
if tp.Field(i).Name == "Age"{
val.Elem().Field(i).SetString("12")
}
}
fmt.Println("~~~~~~~~~~~~~~~~~~")
fmt.Println(per)
}

dump map into struct

将map的数据放入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
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
package reflect_value

import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"reflect"
"strings"
"testing"
)

func Test2123(t *testing.T) {
mList := []map[string]interface{}{
{"Id": 213, "Name": "zhaoliu", "Sex": "男"},
{"Id": 56, "Name": "zhangsan", "Sex": "男"},
{"Id": 7, "Name": "lisi", "Sex": "女"},
{"Id": 978, "Name": "wangwu", "Sex": "男"},
}

type User struct {
Id int
Name string
Sex string
}
users := []*User{}

mapToStruct(mList, &users)
fmt.Printf("users: %+v\n", users)
}

func mapToStruct(mList []map[string]interface{}, model interface{}) (err error) {
val := reflect.Indirect(reflect.ValueOf(model))
typ := val.Type()
for _, r := range mList {
mVal := reflect.Indirect(reflect.New(typ.Elem().Elem())).Addr()
for key, val := range r {
err = setField(mVal.Interface(), key, val)
if err != nil {
return err
}
}
val = reflect.Append(val, mVal)
}
DeepCopy(model, val.Interface())
return err
}

//用map的值替换结构的值
func setField(obj interface{}, name string, value interface{}) error {
// 将首字母转换为大写
sl := strings.Split(name, "")
sl[0] = strings.ToUpper(sl[0])
name = strings.Join(sl, "")

structValue := reflect.ValueOf(obj).Elem() //结构体属性值
//fmt.Printf("structValue: %+v\n", structValue)
structFieldValue := structValue.FieldByName(name) //结构体单个属性值
//fmt.Printf("structFieldValue: %+v\n", structFieldValue)
if !structFieldValue.IsValid() {
return fmt.Errorf("No such field: %s in obj", name)
}

if !structFieldValue.CanSet() {
return fmt.Errorf("Cannot set %s field value", name)
}

structFieldType := structFieldValue.Type() //结构体的类型
val := reflect.ValueOf(value) //map值的反射值

if structFieldType != val.Type() {
return errors.New("type is err")
}

structFieldValue.Set(val)
return nil
}

func DeepCopy(dst, src interface{}) error {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
return err
}
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}

dump interface into 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
package main

import (
"fmt"
"reflect"
)

type Body struct {
Age int
Name string
}

type Message struct {
Body interface{}
}

func main() {
tmp := &Body{
Age: 10086,
Name: "obgnail",
}
message := &Message{Body: tmp}

body := &Body{}
RVmessage := reflect.ValueOf(message.Body).Elem()
reflect.ValueOf(body).Elem().Set(RVmessage)
fmt.Println(body)

//ret := reflect.New(reflect.TypeOf(body).Elem()).Elem()
//ret.Set(RVmessage)
//fmt.Println(ret)
}

reference