Golang Reflection

最近的一个task是要读取环境变量中的配置,于是想到了反射机制。反射机制常常能提供更高维度的视野,可以写出更general的程序。

“reflect”包下主要是Type和Value两个struct:

  • Type封装了“类型”的属性,定义相关的东西找他;
  • Value主要封装了“值”的属性,与值相关的东西找他没错。此外,他是线程安全的(或者叫goroutine安全).

Structs for Case

type Class struct {
    Name    string   `json:"name"`
    Student *Student `json:"student"`
    Grade   int      `json:"grade"`
    school  string   `json:"school"`
}

type Student struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

Cycle

反射机制主要是了解它的整个cycle,才能玩的转。。。

Object->Reflect->Object

s := Student{Name: "LiLei", Age: 20}

typ := reflect.TypeOf(s)
val := reflect.ValueOf(s)
fmt.Printf("The type is %s.\n", typ.String())
fmt.Printf("The name is %s.\n", val.FieldByName("Name").String())

if s, ok := val.Interface().(Student); ok {
    fmt.Printf("The student is %s.\n", s.Name)
} else {
    fmt.Println("Wrong!")
}

// output:
// The type is main.Student.
// The name is LiLei.
// The student is LiLei.

Type->Object

毕竟golang没有jvm那种东西,不能runtime加载。所以type还是得从hard code得到

t := reflect.TypeOf(Student{})
val := reflect.New(t)
fmt.Println(val.Type().String())

// output
// *main.Student

这里reflect.New(reflect.Type)返回的是指向new出的value的指针。

Reflect Operation

使用反射最主要的还是要能操作对象啦

Traverse Object

s := &Student{"LiLei", 18}
c := &Class{"Class A", s, 6, "Century Ave"}

val := reflect.ValueOf(c)
typ := reflect.TypeOf(c)
if val.Kind() == reflect.Ptr {
    fmt.Printf("It is a pointer. Address its value.\n")
    val = val.Elem()
    typ = typ.Elem()
}

for i := 0; i < val.NumField(); i = i + 1 {
    fv := val.Field(i)
    ft := typ.Field(i)
    switch fv.Kind() {
    case reflect.String:
        fmt.Printf("The %d th %s types %s valuing %s with tag env %s\n", i, ft.Name, "string", fv.String(), ft.Tag.Get("env"))
    case reflect.Int:
        fmt.Printf("The %d th %s types %s valuing %d with tag env %s\n", i, ft.Name, "int", fv.Int(), ft.Tag.Get("env"))
    case reflect.Ptr:
        fmt.Printf("The %d th %s types %s valuing %v with tag env %s\n", i, ft.Name, "pointer", fv.Pointer(), ft.Tag.Get("env"))
    }
}

// It is a pointer. Address its value.
// The 0 th Name types string valuing Class A with tag env NAME
// The 1 th Student types pointer valuing 826814776864 with tag env STUDENT
// The 2 th Grade types int valuing 6 with tag env GRADE
// The 3 th school types string valuing Century Ave with tag env SCHOOL

这里,私有的属性也是能遍历到值的。Tag可以为struct附带很多信息,合理利用可以出奇迹啊。

Modify Object

c := &Class{}

val := reflect.ValueOf(c).Elem()
typ := reflect.TypeOf(c).Elem()

for i := 0; i < val.NumField(); i = i + 1 {
    fv := val.Field(i)
    ft := typ.Field(i)
    if !fv.CanSet() {
        fmt.Printf("The %d th %s is unaccessible.\n", i, ft.Name)
        continue
    }

    switch fv.Kind() {
    case reflect.String:
        fv.SetString("LiLei")
    case reflect.Int:
        fv.SetInt(18)
    case reflect.Ptr:
        continue
    }
}

fmt.Printf("%v\n", c)

// output:
// The 3 th school is unaccessible.
// &{LiLei <nil> 18 }

Map/Array/Slice/Channel

Golang的reflect还针对其他几个类型提供了特殊的api。

m := map[string]string{
    "a": "A",
    "b": "B",
}

mv := reflect.ValueOf(m)
for _, k := range mv.MapKeys() {
    v := mv.MapIndex(k)
    fmt.Printf("%s - %s\n", k, v)
}

// output:
// a - A
// b - B

其他类型也有对应的api,具体就查doc吧。

    原文作者:WoodsWalker
    原文地址: https://www.jianshu.com/p/0d346577d32f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞