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吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容