golang學習筆記---reflect包
阿新 • • 發佈:2020-07-30
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 |
package main
import "fmt"
type stringer interface {
Stringer() string
}
func Sprint(x interface {}) {
switch x := x.( type ) {
case stringer:
fmt.Println(x, "is stringer" )
case string:
fmt.Println(x, "is string" )
case int:
fmt.Println( "int" )
default :
fmt.Println( "其他型別" )
}
}
func main() {
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 |
package main
import (
"fmt"
"reflect"
)
type myInt int64
func reflectType(x interface {}) {
t := reflect.TypeOf(x)
fmt.Printf( "type:%v kind:%v\n" , t.Name(), t.Kind())
}
func main() {
var a *float32 // 指標
var b myInt // 自定義型別
var c rune // 類型別名//代表int32
reflectType(a) // type: kind:ptr
reflectType(b) // type:myInt kind:int64
reflectType(c) // type:int32 kind:int32
type person struct {
name string
age int
}
var d = 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 |
package main
import (
"fmt"
"reflect"
)
func reflectType(x interface {}) {
v := reflect.TypeOf(x)
fmt.Printf( "type:%v\n" , v)
}
func main() {
var a float32 = 3.14
reflectType(a) // type:float32
var b 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 |
package main
import (
"fmt"
"reflect"
)
func reflectValue(x interface {}) {
v := reflect.ValueOf(x)
k := v.Kind()
switch k {
case reflect.Int64:
// v.Int()從反射中獲取整型的原始值,然後通過int64()強制型別轉換
fmt.Printf( "type is int64, value is %d\n" , int64(v.Int()))
case reflect.Float32:
// v.Float()從反射中獲取浮點型的原始值,然後通過float32()強制型別轉換
fmt.Printf( "type is float32, value is %f\n" , float32(v.Float()))
case reflect.Float64:
// v.Float()從反射中獲取浮點型的原始值,然後通過float64()強制型別轉換
fmt.Printf( "type is float64, value is %f\n" , float64(v.Float()))
}
}
func main() {
var a float32 = 3.14
var b 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 |
package main
import (
"fmt"
"reflect"
)
func reflectSetValue2(x interface {}) {
v := reflect.ValueOf(x)
// 反射中使用 Elem()方法獲取指標對應的值
if v.Elem().Kind() == reflect.Int64 {
v.Elem().SetInt(200)
}
}
func main() {
var a 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 |
func Dis(path string, v reflect.Value) {
switch v.Kind() {
case reflect.Invalid: // 空
fmt.Printf( "%s= Invalid\n" , path)
case reflect.Slice, reflect.Array:
for i := 0; i<v.Len(); i++{
Dis(fmt.Printf( "%s[%d]" , path, i), v.Index(i))
}
case reflect.Struct:
for i := 0; i< v.NumField(); i++{
feildPath := fmt.Sprintf( "%s.%s" ,path, v.Type().Field(i).Name)
Dis(feildPath, v.Field(i))
}
case reflect.Map:
for _, key := range v.MapKeys(){
fmt.Printf( "%s" , v.MapIndex(key))
}
case reflect.Ptr:
if v.IsNil(){
fmt.Printf( "%s= nil\n" , path)
} else {
Dis(fmt.Sprintf( "*%s" ,path),v.Elem())
}
case reflect.Interface:
if v.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)
}
}
|