最近的一個(gè)task是要讀取環(huán)境變量中的配置冗栗,于是想到了反射機(jī)制。反射機(jī)制常常能提供更高維度的視野,可以寫出更general的程序恨溜。
"reflect"包下主要是Type和Value兩個(gè)struct:
- Type封裝了“類型”的屬性垃杖,定義相關(guān)的東西找他男杈;
- Value主要封裝了“值”的屬性,與值相關(guān)的東西找他沒錯(cuò)调俘。此外伶棒,他是線程安全的(或者叫g(shù)oroutine安全).
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
反射機(jī)制主要是了解它的整個(gè)cycle,才能玩的轉(zhuǎn)彩库。肤无。。
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
使用反射最主要的還是要能操作對(duì)象啦
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還針對(duì)其他幾個(gè)類型提供了特殊的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
其他類型也有對(duì)應(yīng)的api,具體就查doc吧繁扎。