Go筆記-反射

類型Type

整型

s1 := reflect.TypeOf((int8)(0)).String() // int8

等同於下面這個:html

var i8 int8 = 0
reflect.TypeOf(i8).String() // int8

指針

reflect.TypeOf((*int8)(nil)).String() // *int8

等同於:golang

var i8 *int8 = nil
reflect.TypeOf(i8).String() // *int8

指針指向元素的類型

reflect.TypeOf((*int8)(nil)).Elem().String() // int8

結構體

st := (*struct {
	a int
})(nil)
reflect.TypeOf(st).String() // *struct { a int }
reflect.TypeOf(st).Elem().String() // struct { a int }

結構體字段

reflect.TypeOf(st).Elem().Field(0).Type.String() // int

	f, found := reflect.TypeOf(st).Elem().FieldByName("a")
	if !found {
		fmt.Println("no field 'a'.")
	} else {
		fmt.Println(f.Type.String()) // int
	}

測試指針和非指針引用的結構體獲取字段和方法的能力

func main() {
	var i1 interface{}
	i1 = &T{"t1"}

	var i2 interface{}
	i2 = T{"t2"}

	v1 := reflect.ValueOf(i1)
	v2 := reflect.ValueOf(i2)

	// 字段
	//	fmt.Println(v1.FieldByName("A")) // err: call of reflect.Value.FieldByName on ptr Value
	fmt.Println(v2.FieldByName("A"))

	// 方法
	fmt.Println(v1.MethodByName("Fun1"))
	fmt.Println(v1.MethodByName("Fun2"))

	fmt.Println(v2.MethodByName("Fun1"))
	fmt.Println(v2.MethodByName("Fun2")) // <invalid Value>
	
	if m := v2.MethodByName("Fun2"); m.IsValid() {
		fmt.Println("v2.Fun2() is valid.")
	} else {
		fmt.Println("v2.Fun2() is invalid") // this one
	}
}

type T struct {
	A string
}

func (t T) Fun1() {

}

func (t *T) Fun2() {

}

指針不能獲取到字段值。數組

非指針不能獲取到接受者是指針的方法,但反之卻能夠。app

獲取到方法後判斷一下是否是可用(IsValid())函數

數組

reflect.TypeOf([32]int{}).String() // [32]int

數組元素類型

reflect.TypeOf([32]int{}).Elem().String() // int

map

maptype := reflect.TypeOf((map[string]*int32)(nil))
maptype.String()        // map[string]*int32
maptype.Key().String()  // string
maptype.Elem().String() // *int32

chan

chantype := reflect.TypeOf((chan<- string)(nil))
chantype.String()        // chan<- string
chantype.Elem().String() // string

接口

得到接口的值的真實類型

var inter struct {
	E interface{}
}
inter.E = 123.456

innerPtrValue := reflect.ValueOf(&inter)
eField := innerPtrValue.Elem().Field(0)
eField.Type() // interface {}

reflect.ValueOf(eField.Interface()).Type() // float64

或者測試

eField.Elem().Type()

判斷interface{}指向的元素的類型

var i interface{}
	i = "hello"

	t := reflect.ValueOf(i).Type()
	fmt.Println(t) // string

但若是傳給i的是個指針的話:this

var i interface{}
	s := "hello"
	i = &s

	t := reflect.ValueOf(i).Type()
	fmt.Println(t) // *string

若是使用reflect.ValueOf(i).Elem().Type()能夠得到,可是這個對於不是指針的話就會報錯,由於Elem()不能用於string指針

可使用reflect.Indirect():code

var i interface{}
	s := "hello"
	i = &s // i = s也是輸出string

	v := reflect.ValueOf(i)
	t := reflect.Indirect(v).Type()
	fmt.Println(t) // string

下面是Indirect()的實現:htm

func Indirect(v Value) Value {
	if v.Kind() != Ptr {
		return v
	}
	return v.Elem()
}

能夠參考一下接口爲nil時類型和值都必須是nil的介紹。

Kind

reflect.TypeOf((int)(0)).Kind() == reflect.Int
reflect.TypeOf((*int)(nil)).Kind() == reflect.Ptr
reflect.TypeOf((map[string]string)(nil)).Kind() == reflect.Map

反射對象的 Kind 描述了底層類型,而不是靜態類型。若是一個反射對象包含了用戶定義的整數類型的值,就像:

type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)

v 的 Kind 仍然是 reflect.Int,儘管 x 的靜態類型是 MyInt,而不是 int。換句話說,Kind 沒法從 MyInt 中區分 int,而 Type 能夠。

獲取函數名

func getFuncName(f interface{}) string {
	longName := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
	parts := strings.Split(longName, ".")
	if len(parts) > 0 {
		return parts[len(parts)-1]
	}
	return "?"
}

值 Value

設置值

SetXXX系列函數要求CanSet()返回true。

先判斷類型在設置值

i := 123
iptr := &i
v := reflect.ValueOf(iptr).Elem()
if v.Kind() == reflect.Int {
	v.SetInt(321)
}
fmt.Println(i) // 321

不判斷類型直接設置值

var i int = 123
iptr := &i
v := reflect.ValueOf(iptr).Elem()
v.Set(reflect.ValueOf(int(321)))
fmt.Println(i) // 321

可設置性

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.

這裏報錯是由於 v 沒法設置值給 x。

Value的 CanSet 方法提供了值的設置性;在這個例子中,

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:" , v.CanSet()) // false

建立 v 時傳遞的是 x 的副本給 reflect.ValueOf(),而不是 x 自己。

var x float64 = 3.4
p := reflect.ValueOf(&x) // 注意:獲取 X 的地址。
fmt.Println("type of p:", p.Type()) // type of p: *float64
fmt.Println("settability of p:" , p.CanSet()) // settability of p: false

反射對象 p 並非可設置的,並且咱們也不但願設置 p,其實是 *p。爲了得到 p 指向的內容,調用值上的 Elem 方法,從指針間接指向,而後保存反射值的結果叫作 v:

v := p.Elem()
fmt.Println("settability of v:" , v.CanSet()) // true
v.setFloat(7.1)

經過 reflect.Indirect() 方法來取指針指向的具體值更好。

設置和讀取數組的值

arrval := reflect.ValueOf(&[...]int{1, 2, 3}).Elem()
arrval.Index(1).SetInt(5)

out := "["
for i := 0; i < arrval.Len(); i++ {
	out += strconv.Itoa(int(arrval.Index(i).Int())) + ","
}
out += "]"
fmt.Println(out) // [1,5,3,]

ValueOf()的參數需是數組指針,下面這個是錯誤的

reflect.ValueOf([...]int{1, 2, 3})

對於切片來講則不須要這麼麻煩(由於經過切片獲取到的數組是指針)

arrval := reflect.ValueOf([]int{1, 2, 3})
arrval.Index(1).SetInt(5)

經過指針設置值

var ip *int32
var i int32 = 123
vip := reflect.ValueOf(&ip)      // **int32
vi := reflect.ValueOf(&i).Elem() // 非得這樣嗎?(直接傳遞i給ValueOf會複製i)
vip.Elem().Set(vi.Addr()) // *int32.Set(*int32)
fmt.Println(*ip) // 123

將指針設置爲零值

var i int32 = 123
ip := &i

vp := reflect.ValueOf(&ip).Elem()
vp.Set(reflect.Zero(vp.Type()))
fmt.Println(ip) // <nil>

設置map

設置map爲空值

m := make(map[string]int)
m["a"], m["b"] = 1, 2

mp := reflect.ValueOf(&m).Elem()
mp.Set(reflect.Zero(mp.Type()))
fmt.Println(m)        // map[]
fmt.Println(m == nil) // true

interface{}

從匿名結構體經過Interface()還原到實際類型

b := struct{ a, b, c, d int64 }{1, 2, 3, 4}
v := reflect.ValueOf(b)
b1 := v.Interface().(struct {
	a, b, c, d int64
})
fmt.Println(b1) // {1 2 3 4}

interface()不可用於結構體中未導出字段

var st struct {
	PublicField  string
	privateField string
}

vSt := reflect.ValueOf(&st).Elem()
vSt.Field(0).IsValid() // true
vSt.Field(1).IsValid() // true

defer func() {
	if err := recover(); err != nil {
		fmt.Println("has error :", err)
	}
}()
//vSt.Field(0).Interface() // 沒有panic
vSt.Field(1).Interface() // panic

標籤Tag

var st struct {
	f1 string `tag1:"cont1" tag2:cont2 tag3:3`
}
var f1Tag = reflect.TypeOf(st).Field(0).Tag
f1Tag.Get("tag1") // cont1
f1Tag.Get("tag2") // 空字符串
f1Tag.Get("tag3") // 空字符串

reflect.Copy()

copy slice:

a := []int{1, 2, 3, 4, 5, 6}
b := []int{11, 12, 13, 14, 15}
aa := reflect.ValueOf(&a).Elem()
bb := reflect.ValueOf(&b).Elem()
aa.SetLen(4) // 限制長度只到第4個
reflect.Copy(bb, aa) // 賦值aa的前4個到b裏
aa.SetLen(6) // 回覆長度
fmt.Println(a) // [1 2 3 4 5 6]
fmt.Println(b) // [1 2 3 4 15]

copy array:

a := [...]int{1, 2, 3}
b := [...]int{11, 12, 13, 14, 15}
aa := reflect.ValueOf(&a).Elem()
bb := reflect.ValueOf(&b).Elem()
reflect.Copy(bb, aa)
fmt.Println(b) // [1 2 3 14 15]

結構體的字段

遍歷結構體的字段

func main() {
	t := &T{"a", "b"}
	val := reflect.Indirect(reflect.ValueOf(t))
	typ := val.Type()

	for i := 0; i < val.NumField(); i++ {
		f := typ.Field(i)
		fmt.Println(f.Name)
	}
}

type T struct {
	PublicField  string
	privateField string
}

輸出:

PublicField
privateField

判斷結構體字段是不是導出的

t := &T{"a", "b"}
val := reflect.Indirect(reflect.ValueOf(t))
typ := val.Type()
typepath := typ.PkgPath()
fmt.Println("type pkgPath: ", typepath) // type pkgPath:  main

for i := 0; i < val.NumField(); i++ {
	f := typ.Field(i)
	p := f.PkgPath
	fmt.Println(f.Name, " => ", p)
}

//	 PublicField  =>
//	 privateField  =>  main

Select

文檔: https://golang.org/pkg/reflect/#Select

和select結構功能同樣,但這個能夠直接指定數組。

測試代碼:

import (
	"fmt"
	"reflect"
)

func main() {
	selectCase := make([]reflect.SelectCase, 3)

	for i := 0; i < 3; i++ {
		c := make(chan string)

		selectCase[i].Dir = reflect.SelectRecv
		selectCase[i].Chan = reflect.ValueOf(c)

		go func(i int, c chan string) {
			for j := 0; j < 5; j++ {
				c <- fmt.Sprintf("groutine#%d send %d", i, j)
			}
			fmt.Printf("groutine#%d closed\n", i)
			close(c)
		}(i, c)
	}

	done := 0
	finished := 0
	for finished < len(selectCase) {
		chosen, recv, recvOk := reflect.Select(selectCase)
		if recvOk {
			done++
			fmt.Printf("receive #%d, value=%s\n", chosen, recv.String())
		} else {
			fmt.Printf("#%d closed\n", chosen)
			selectCase = append(selectCase[:chosen], selectCase[chosen+1:]...)
		}
	}

	fmt.Println("done:", done) // 15
}

indirect

// From html/template/content.go
// Copyright 2011 The Go Authors. All rights reserved.
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
func indirect(a interface{}) interface{} {
	if a == nil {
		return nil
	}
	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
		// Avoid creating a reflect.Value if it's not a pointer.
		return a
	}
	v := reflect.ValueOf(a)
	for v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}
相關文章
相關標籤/搜索