1. 程式人生 > 實用技巧 >golang學習筆記---reflect包

golang學習筆記---reflect包

go語言提供了一種機制,在編譯時不知道型別的情況下,可更新變數,在執行時檢視值,呼叫方法以及直接對他們的佈局進行操作。這種機制稱為反射(reflection)。

為什麼使用反射

  有時候我們需要寫一個函式有能力統一處理各種值型別的函式,而這些型別可能無法共享同一個介面,也可能佈局未知,也有可能這個型別在我們設計函式時還不存在。甚至這個類會同時存在上面三個問題。假設我們設計一個例子來判斷,如下

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 packagemain import"fmt" type
stringerinterface{ Stringer() string } funcSprint(xinterface{}) { switchx := x.(type) { casestringer: fmt.Println(x,"is stringer") casestring: fmt.Println(x,"is string") caseint: fmt.Println("int") default: fmt.Println("其他型別") } } funcmain() { Sprint("fd") }

 這時你會發現,如果型別很多或者更多不確定的型別就會很麻煩。所以我們引入了reflect包。

reflect

  反射功能由reflect包提供,它定義了兩個重要的型別:Type和Value,並且TypeOf和ValueOf

  • Type: 這是一個介面,真正使用該介面的例項是reflect.rtype,該例項持有動態型別的所有資訊。並且提供下面方法
    • kind(): 獲取具體型別的底層型別。
    • Elem(): 這個方法返回的是原始變數的元素的型別。
  • Value: 這是一個struct,持有動態值的所有資訊。
    • Type(): Type方法返回介面變數的動態型別資訊,也就是傳入ValueOf方法的原始變數的型別。
    • Kind(): 與Type的kind方法一樣,返回的是原始型別。
    • Interface():把一個reflect.Value物件還原回一個空介面型別的變數,可以通過型別斷言:x, ok := v.Interface().(int) 
    • Elem(): 呼叫該方法的Value物件

 type.kind() &type.name()

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 packagemain import( "fmt" "reflect" ) typemyInt int64 funcreflectType(xinterface{}) { t := reflect.TypeOf(x) fmt.Printf("type:%v kind:%v\n", t.Name(), t.Kind()) } funcmain() { vara *float32// 指標 varb myInt// 自定義型別 varc rune// 類型別名//代表int32 reflectType(a)// type: kind:ptr reflectType(b)// type:myInt kind:int64 reflectType(c)// type:int32 kind:int32 typepersonstruct{ name string age int } vard = person{ name:"wang", age: 18, } reflectType(d)// type:person kind:struct }

TypeOf

  TypeOf函式接收任何的interface{}引數,並且把介面中的動態型別以reflect.Type形式返回。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 packagemain import( "fmt" "reflect" ) funcreflectType(xinterface{}) { v := reflect.TypeOf(x) fmt.Printf("type:%v\n", v) } funcmain() { vara float32 = 3.14 reflectType(a)// type:float32 varb int64 = 100 reflectType(b)// type:int64 }

ValueOf 

  ValueOf函式可以接收任意的interface{}並將介面的動態值以reflect.Value的形式返回。與reflect.TypeOf類似,reflect.ValueOf的返回值也是具體值,不過reflect.Value也可以包含一個介面值。

  對於不同型別,我們用reflect.Value的kind方法來區分不同型別。但型別的分類(kind)只有少數幾種:

  • 基礎型別: Bool, String以及數字型別
  • 聚合型別:Array, struct
  • 引用型別:Chan, Func, Ptr, Slice, Map
  • 介面型別:interface
  • Invalid型別:表示沒有任何值。reflect.Value的零值就屬於Invalid型別。
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 packagemain import( "fmt" "reflect" ) funcreflectValue(xinterface{}) { v := reflect.ValueOf(x) k := v.Kind() switchk { casereflect.Int64: // v.Int()從反射中獲取整型的原始值,然後通過int64()強制型別轉換 fmt.Printf("type is int64, value is %d\n", int64(v.Int())) casereflect.Float32: // v.Float()從反射中獲取浮點型的原始值,然後通過float32()強制型別轉換 fmt.Printf("type is float32, value is %f\n", float32(v.Float())) casereflect.Float64: // v.Float()從反射中獲取浮點型的原始值,然後通過float64()強制型別轉換 fmt.Printf("type is float64, value is %f\n", float64(v.Float())) } } funcmain() { vara float32 = 3.14 varb int64 = 100 reflectValue(a)// type is float32, value is 3.140000 reflectValue(b)// type is int64, value is 100 // 將int型別的原始值轉換為reflect.Value型別 c := reflect.ValueOf(10) fmt.Printf("type c :%T\n", c)// type c :reflect.Value } 

value.Elem設定值

  上面知識獲取了值及型別,如果想要修改,可以通過elem,但必須傳遞的是指標

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 packagemain import( "fmt" "reflect" ) funcreflectSetValue2(xinterface{}) { v := reflect.ValueOf(x) // 反射中使用 Elem()方法獲取指標對應的值 ifv.Elem().Kind() == reflect.Int64 { v.Elem().SetInt(200) } } funcmain() { vara int64 = 100 reflectSetValue2(&a) fmt.Println(a) }

 最後整理一下常用的型別判斷,如 

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 funcDis(path string, v reflect.Value) { switchv.Kind() { casereflect.Invalid:// 空 fmt.Printf("%s= Invalid\n", path) casereflect.Slice, reflect.Array: fori := 0; i<v.Len(); i++{ Dis(fmt.Printf("%s[%d]", path, i), v.Index(i)) } casereflect.Struct: fori := 0; i< v.NumField(); i++{ feildPath := fmt.Sprintf("%s.%s",path, v.Type().Field(i).Name) Dis(feildPath, v.Field(i)) } casereflect.Map: for_, key :=rangev.MapKeys(){ fmt.Printf("%s", v.MapIndex(key)) } casereflect.Ptr: ifv.IsNil(){ fmt.Printf("%s= nil\n", path) }else{ Dis(fmt.Sprintf("*%s",path),v.Elem()) } casereflect.Interface: ifv.IsNil(){ fmt.Printf("%s=nil\n", path) }else{ fmt.Printf("%s.type=%s\n",path, v.Elem().Type()) } default:// 基礎型別,chan, 函式 fmt.Printf("%s\n",path) } }