1 Preface
反射是程序在运行时
检查变量和值, 并获取到它们的类型的能力。
很多主流语言都提供了反射语法特性,比如Java, python等; C++语言本身不支持反射,
但是第三方库实现了反射特性, 比如 google 的 protobuf。
如果支持反射,解决某些场景的问题,可以变得简单。
比如通过struct/class, 生成对应的建表语句,根据生成 insert 语句(批量将CSV导入数据库)等。
在之前的文章中,我们了解了如何在 Java/C++ 中使用反射:
2 Go 反射
Go语言中使用空的接口,表示任一类型(可以理解为Any, 或者Java 中的 Object)
Go 反射的包,主要用到以下API:
- reflect.Type 和 reflect.Value
- reflect.Kind
- NumField()和Field()方法
- Int()和String()方法
(1) 获取对象的类型
var tp reflect.Type = reflect.TypeOf(p)
(2) 获取对象的值
var v reflect.Value = reflect.ValueOf(p)
3 完整代码
完整代码如下:
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
|
package reflection
import (
"fmt"
"reflect"
"testing"
)
type order struct {
orderId int
customName string
}
func reflectStruct(p interface{}) {
// 获取类型
tp := reflect.TypeOf(p)
v := reflect.ValueOf(p)
fmt.Println("Type ", tp)
fmt.Println(v)
k := tp.Kind()
// Kind = struct Size = 24
fmt.Println("Kind =", k, "Size =", tp.Size())
// 如果为结构体类型
if tp.Kind() == reflect.Struct {
val := reflect.ValueOf(p)
// 获取字段个数
fmt.Println("field count:", val.NumField())
// 遍历每一个字段
for i := 0; i < val.NumField(); i++ {
element := val.Field(i)
fmt.Println("type:", element.Kind(), "value:", element)
switch element.Kind() {
case reflect.Int:
//提取对应的值,为对应的类型
id := element.Int()
fmt.Printf("===> is_int %T %d \n", id, id)
case reflect.String:
str := element.String()
fmt.Printf("===> is_string %T %s", str, str)
default:
fmt.Println("type is not support:", element.Kind())
}
}
}
}
//Clear is better than clever. Reflection is never clear.
func TestReflection(t *testing.T) {
o := order{
orderId: 10,
customName: "Alice",
}
reflectStruct(o)
}
|
4 总结
反射很强大,我们可以在合适的场景下使用它,比如输出结构体内部数据,
根据Schema生成SQL语句等。
但是我们有时候需要保证代码的清晰,以及效率; 在关键路径上斟酌使用。
借用Rob Pike
关于 reflection 的格言作为本文的结束。
Clear is better than clever. Reflection is never clear.
《全文完》