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.

《全文完》