Go是一門(mén)具有良好反射支持的靜態(tài)語(yǔ)言。 本文將解釋?reflect
?標(biāo)準(zhǔn)庫(kù)包中提供的反射功能。
在閱讀本剩下的部分之前,最好先閱讀Go類(lèi)型系統(tǒng)概述和接口兩篇文章。
Go中提供的反射功能帶來(lái)了很多動(dòng)態(tài)特性。 很多標(biāo)準(zhǔn)庫(kù),比如fmt
和很多encoding
包,均十分依賴(lài)于反射機(jī)制。
我們可以通過(guò)reflect
庫(kù)包中Type
和Value
兩個(gè)類(lèi)型提供的功能來(lái)觀察不同的Go值。 本文下面的內(nèi)容將介紹如何使用這兩個(gè)類(lèi)型。
Go反射機(jī)制設(shè)計(jì)的目標(biāo)之一是任何非反射操作都可以通過(guò)反射機(jī)制來(lái)完成。 由于各種各樣的原因,此目標(biāo)并沒(méi)有得到100%的實(shí)現(xiàn)。 但是,目前大部分的非反射操作都可以通過(guò)反射機(jī)制來(lái)完成。 另一方面,通過(guò)反射,我們也可以完成一些使用非反射操作不可能完成的操作。 不能或者只能通過(guò)反射完成的操作將在下面的講解中提及。
通過(guò)調(diào)用reflect.TypeOf
函數(shù),我們可以從一個(gè)任何非接口類(lèi)型的值創(chuàng)建一個(gè)reflect.Type
值。 此reflect.Type
值表示著此非接口值的類(lèi)型。通過(guò)此值,我們可以得到很多此非接口類(lèi)型的信息。 當(dāng)然,我們也可以將一個(gè)接口值傳遞給一個(gè)reflect.TypeOf
函數(shù)調(diào)用,但是此調(diào)用將返回一個(gè)表示著此接口值的動(dòng)態(tài)類(lèi)型的reflect.Type
值。 實(shí)際上,reflect.TypeOf
函數(shù)的唯一參數(shù)的類(lèi)型為interface{}
, reflect.TypeOf
函數(shù)將總是返回一個(gè)表示著此唯一接口參數(shù)值的動(dòng)態(tài)類(lèi)型的reflect.Type
值。
那如何得到一個(gè)表示著某個(gè)接口類(lèi)型的reflect.Type
值呢? 我們必須通過(guò)下面將要介紹的一些間接途徑來(lái)達(dá)到這一目的。
類(lèi)型reflect.Type
為一個(gè)接口類(lèi)型,它指定了若干方法。 通過(guò)這些方法,我們能夠觀察到一個(gè)reflect.Type
值所表示的Go類(lèi)型的各種信息。 這些方法中的有些適用于所有種類(lèi)的類(lèi)型,有些只適用于一種或幾種類(lèi)型。
通過(guò)不合適的reflect.Type
屬主值調(diào)用某個(gè)方法將在運(yùn)行時(shí)產(chǎn)生一個(gè)恐慌。 請(qǐng)閱讀reflect
代碼庫(kù)中各個(gè)方法的文檔來(lái)獲取如何正確地使用這些方法。
一個(gè)例子:
package main
import "fmt"
import "reflect"
func main() {
type A = [16]int16
var c <-chan map[A][]byte
tc := reflect.TypeOf(c)
fmt.Println(tc.Kind()) // chan
fmt.Println(tc.ChanDir()) // <-chan
tm := tc.Elem()
ta, tb := tm.Key(), tm.Elem()
fmt.Println(tm.Kind(), ta.Kind(), tb.Kind()) // map array slice
tx, ty := ta.Elem(), tb.Elem()
// byte是uint8類(lèi)型的別名。
fmt.Println(tx.Kind(), ty.Kind()) // int16 uint8
fmt.Println(tx.Bits(), ty.Bits()) // 16 8
fmt.Println(tx.ConvertibleTo(ty)) // true
fmt.Println(tb.ConvertibleTo(ta)) // false
// 切片類(lèi)型和映射類(lèi)型都是不可比較類(lèi)型。
fmt.Println(tb.Comparable()) // false
fmt.Println(tm.Comparable()) // false
fmt.Println(ta.Comparable()) // true
fmt.Println(tc.Comparable()) // true
}
目前,Go支持26種種類(lèi)的類(lèi)型。
在上面這個(gè)例子中,我們使用方法Elem
來(lái)得到某些類(lèi)型的元素類(lèi)型。 實(shí)際上,此方法也可以用來(lái)得到一個(gè)指針類(lèi)型的基類(lèi)型。一個(gè)例子:
package main
import "fmt"
import "reflect"
type T []interface{m()}
func (T) m() {}
func main() {
tp := reflect.TypeOf(new(interface{}))
tt := reflect.TypeOf(T{})
fmt.Println(tp.Kind(), tt.Kind()) // ptr slice
// 使用間接的方法得到表示兩個(gè)接口類(lèi)型的reflect.Type值。
ti, tim := tp.Elem(), tt.Elem()
fmt.Println(ti.Kind(), tim.Kind()) // interface interface
fmt.Println(tt.Implements(tim)) // true
fmt.Println(tp.Implements(tim)) // false
fmt.Println(tim.Implements(tim)) // true
// 所有的類(lèi)型都實(shí)現(xiàn)了任何空接口類(lèi)型。
fmt.Println(tp.Implements(ti)) // true
fmt.Println(tt.Implements(ti)) // true
fmt.Println(tim.Implements(ti)) // true
fmt.Println(ti.Implements(ti)) // true
}
上面這個(gè)例子同時(shí)也展示了如何通過(guò)間接的途徑得到一個(gè)表示一個(gè)接口類(lèi)型的reflect.Type
值。
我們可以通過(guò)反射列出一個(gè)類(lèi)型的所有方法和一個(gè)結(jié)構(gòu)體類(lèi)型的所有(導(dǎo)出和非導(dǎo)出)字段的類(lèi)型。 我們也可以通過(guò)反射列出一個(gè)函數(shù)類(lèi)型的各個(gè)輸入?yún)?shù)和返回結(jié)果類(lèi)型。
package main
import "fmt"
import "reflect"
type F func(string, int) bool
func (f F) m(s string) bool {
return f(s, 32)
}
func (f F) M() {}
type I interface{m(s string) bool; M()}
func main() {
var x struct {
F F
i I
}
tx := reflect.TypeOf(x)
fmt.Println(tx.Kind()) // struct
fmt.Println(tx.NumField()) // 2
fmt.Println(tx.Field(1).Name) // i
// 包路徑(PkgPath)是非導(dǎo)出字段(或者方法)的內(nèi)在屬性。
fmt.Println(tx.Field(0).PkgPath) //
fmt.Println(tx.Field(1).PkgPath) // main
tf, ti := tx.Field(0).Type, tx.Field(1).Type
fmt.Println(tf.Kind()) // func
fmt.Println(tf.IsVariadic()) // false
fmt.Println(tf.NumIn(), tf.NumOut()) // 2 1
t0, t1, t2 := tf.In(0), tf.In(1), tf.Out(0)
// 下一行打印出:string int bool
fmt.Println(t0.Kind(), t1.Kind(), t2.Kind())
fmt.Println(tf.NumMethod(), ti.NumMethod()) // 1 2
fmt.Println(tf.Method(0).Name) // M
fmt.Println(ti.Method(1).Name) // m
_, ok1 := tf.MethodByName("m")
_, ok2 := ti.MethodByName("m")
fmt.Println(ok1, ok2) // false true
}
從上面這個(gè)例子我們可以看出:
reflect.Type.NumMethod
方法只返回一個(gè)類(lèi)型的所有導(dǎo)出的方法(包括通過(guò)內(nèi)嵌得來(lái)的隱式方法)的個(gè)數(shù),并且 方法reflect.Type.MethodByName
不能用來(lái)獲取一個(gè)類(lèi)型的非導(dǎo)出方法; 而對(duì)于接口類(lèi)型,則并無(wú)這些限制(Go 1.16之前的文檔對(duì)這兩個(gè)方法的描述不準(zhǔn)確,并沒(méi)有體現(xiàn)出這個(gè)差異)。 此情形同樣存在于下一節(jié)將要介紹的reflect.Value
類(lèi)型上的相應(yīng)方法。reflect.Type.NumField
方法返回一個(gè)結(jié)構(gòu)體類(lèi)型的所有字段(包括非導(dǎo)出字段)的數(shù)目,但是不推薦使用方法reflect.Type.FieldByName
來(lái)獲取非導(dǎo)出字段。我們可以通過(guò)反射來(lái)檢視結(jié)構(gòu)體字段的標(biāo)簽信息。 結(jié)構(gòu)體字段標(biāo)簽的類(lèi)型為reflect.StructTag
,它的方法Get
和Lookup
用來(lái)檢視字段標(biāo)簽中的鍵值對(duì)。 一個(gè)例子:
package main
import "fmt"
import "reflect"
type T struct {
X int `max:"99" min:"0" default:"0"`
Y, Z bool `optional:"yes"`
}
func main() {
t := reflect.TypeOf(T{})
x := t.Field(0).Tag
y := t.Field(1).Tag
z := t.Field(2).Tag
fmt.Println(reflect.TypeOf(x)) // reflect.StructTag
// v的類(lèi)型為string
v, present := x.Lookup("max")
fmt.Println(len(v), present) // 2 true
fmt.Println(x.Get("max")) // 99
fmt.Println(x.Lookup("optional")) // false
fmt.Println(y.Lookup("optional")) // yes true
fmt.Println(z.Lookup("optional")) // yes true
}
注意:
`optional: "yes"`
不形成鍵值對(duì)。`json:"author, omitempty“`
、
`json:" author,omitempty“`
以及
`json:"author,omitempty“`
各不相同。 reflect
代碼包也提供了一些其它函數(shù)來(lái)動(dòng)態(tài)地創(chuàng)建出來(lái)一些無(wú)名組合類(lèi)型。
package main
import "fmt"
import "reflect"
func main() {
ta := reflect.ArrayOf(5, reflect.TypeOf(123))
fmt.Println(ta) // [5]int
tc := reflect.ChanOf(reflect.SendDir, ta)
fmt.Println(tc) // chan<- [5]int
tp := reflect.PtrTo(ta)
fmt.Println(tp) // *[5]int
ts := reflect.SliceOf(tp)
fmt.Println(ts) // []*[5]int
tm := reflect.MapOf(ta, tc)
fmt.Println(tm) // map[[5]int]chan<- [5]int
tf := reflect.FuncOf([]reflect.Type{ta},
[]reflect.Type{tp, tc}, false)
fmt.Println(tf) // func([5]int) (*[5]int, chan<- [5]int)
tt := reflect.StructOf([]reflect.StructField{
{Name: "Age", Type: reflect.TypeOf("abc")},
})
fmt.Println(tt) // struct { Age string }
fmt.Println(tt.NumField()) // 1
}
上面的例子并未展示和reflect.Type
相關(guān)的所有函數(shù)和方法。 請(qǐng)閱讀reflect
標(biāo)準(zhǔn)庫(kù)代碼包的文檔以獲取如何使用這些函數(shù)和方法。
注意,到目前為止(Go 1.19),我們無(wú)法通過(guò)反射動(dòng)態(tài)創(chuàng)建一個(gè)接口類(lèi)型。這是Go反射目前的一個(gè)限制。
另一個(gè)限制是使用反射動(dòng)態(tài)創(chuàng)建結(jié)構(gòu)體類(lèi)型的時(shí)候可能會(huì)有各種不完美的情況出現(xiàn)。
第三個(gè)限制是我們無(wú)法通過(guò)反射來(lái)聲明一個(gè)新的類(lèi)型。
類(lèi)似的,我們可以通過(guò)調(diào)用reflect.ValueOf
函數(shù),從一個(gè)非接口類(lèi)型的值創(chuàng)建一個(gè)reflect.Value
值。 此reflect.Value
值代表著此非接口值。 和reflect.TypeOf
函數(shù)類(lèi)似,reflect.ValueOf
函數(shù)也只有一個(gè)interface{}
類(lèi)型的參數(shù)。 當(dāng)我們將一個(gè)接口值傳遞給一個(gè)reflect.ValueOf
函數(shù)調(diào)用時(shí),此調(diào)用返回的是代表著此接口值的動(dòng)態(tài)值的一個(gè)reflect.Value
值。
我們必須通過(guò)間接的途徑獲得一個(gè)代表一個(gè)接口值的reflect.Value
值。
被一個(gè)reflect.Value
值代表著的值常稱(chēng)為此reflect.Value
值的底層值(underlying value)。
reflect.Value
類(lèi)型有很多方法。 我們可以調(diào)用這些方法來(lái)觀察和操縱一個(gè)reflect.Value
屬主值表示的Go值。 這些方法中的有些適用于所有種類(lèi)類(lèi)型的值,有些只適用于一種或幾種類(lèi)型的值。 通過(guò)不合適的reflect.Value
屬主值調(diào)用某個(gè)方法將在運(yùn)行時(shí)產(chǎn)生一個(gè)恐慌。 請(qǐng)閱讀reflect
代碼庫(kù)中各個(gè)方法的文檔來(lái)獲取如何正確地使用這些方法。
一個(gè)reflect.Value
值的CanSet
方法將返回此reflect.Value
值代表的Go值是否可以被修改(可以被賦值)。 如果一個(gè)Go值可以被修改,則我們可以調(diào)用對(duì)應(yīng)的reflect.Value
值的Set
方法來(lái)修改此Go值。 注意:reflect.ValueOf
函數(shù)直接返回的reflect.Value
值都是不可修改的。
一個(gè)例子:
package main
import "fmt"
import "reflect"
func main() {
n := 123
p := &n
vp := reflect.ValueOf(p)
fmt.Println(vp.CanSet(), vp.CanAddr()) // false false
vn := vp.Elem() // 取得vp的底層指針值引用的值的代表值
fmt.Println(vn.CanSet(), vn.CanAddr()) // true true
vn.Set(reflect.ValueOf(789)) // <=> vn.SetInt(789)
fmt.Println(n) // 789
}
一個(gè)結(jié)構(gòu)體值的非導(dǎo)出字段不能通過(guò)反射來(lái)修改。
package main
import "fmt"
import "reflect"
func main() {
var s struct {
X interface{} // 一個(gè)導(dǎo)出字段
y interface{} // 一個(gè)非導(dǎo)出字段
}
vp := reflect.ValueOf(&s)
// 如果vp代表著一個(gè)指針,下一行等價(jià)于"vs := vp.Elem()"。
vs := reflect.Indirect(vp)
// vx和vy都各自代表著一個(gè)接口值。
vx, vy := vs.Field(0), vs.Field(1)
fmt.Println(vx.CanSet(), vx.CanAddr()) // true true
// vy is addressable but not modifiable.
fmt.Println(vy.CanSet(), vy.CanAddr()) // false true
vb := reflect.ValueOf(123)
vx.Set(vb) // okay, 因?yàn)関x代表的值是可修改的。
// vy.Set(vb) // 會(huì)造成恐慌,因?yàn)関y代表的值是不可修改的。
fmt.Println(s) // {123
fmt.Println(vx.IsNil(), vy.IsNil()) // false true
}
上例中同時(shí)也展示了如何間接地獲取底層值為接口值的reflect.Value
值。
從上兩例中,我們可以得知有兩種方法獲取一個(gè)代表著一個(gè)指針?biāo)弥闹档?code>reflect.Value值:
reflect.Value
值的Elem
方法。
reflect.Value
值的傳遞給一個(gè)reflect.Indirect
函數(shù)調(diào)用。
(如果傳遞給一個(gè)reflect.Indirect
函數(shù)調(diào)用的實(shí)參不代表著一個(gè)指針值,則此調(diào)用返回此實(shí)參的一個(gè)復(fù)制。)
注意:reflect.Value.Elem
方法也可以用來(lái)獲取一個(gè)代表著一個(gè)接口值的動(dòng)態(tài)值的reflect.Value
值,比如下例中所示。
package main
import "fmt"
import "reflect"
func main() {
var z = 123
var y = &z
var x interface{} = y
v := reflect.ValueOf(&x)
vx := v.Elem()
vy := vx.Elem()
vz := vy.Elem()
vz.Set(reflect.ValueOf(789))
fmt.Println(z) // 789
}
reflect
標(biāo)準(zhǔn)庫(kù)包中也提供了一些對(duì)應(yīng)著內(nèi)置函數(shù)或者各種非反射功能的函數(shù)。 下面這個(gè)例子展示了如何利用這些函數(shù)將一個(gè)(效率不高的)自定義泛型函數(shù)綁定到不同的類(lèi)型的函數(shù)值上。
package main
import "fmt"
import "reflect"
func InvertSlice(args []reflect.Value) []reflect.Value {
inSlice, n := args[0], args[0].Len()
outSlice := reflect.MakeSlice(inSlice.Type(), 0, n)
for i := n-1; i >= 0; i-- {
element := inSlice.Index(i)
outSlice = reflect.Append(outSlice, element)
}
return []reflect.Value{outSlice}
}
func Bind(p interface{},
f func ([]reflect.Value) []reflect.Value) {
// invert代表著一個(gè)函數(shù)值。
invert := reflect.ValueOf(p).Elem()
invert.Set(reflect.MakeFunc(invert.Type(), f))
}
func main() {
var invertInts func([]int) []int
Bind(&invertInts, InvertSlice)
fmt.Println(invertInts([]int{2, 3, 5})) // [5 3 2]
var invertStrs func([]string) []string
Bind(&invertStrs, InvertSlice)
fmt.Println(invertStrs([]string{"Go", "C"})) // [C Go]
}
如果一個(gè)reflect.Value
值的底層值為一個(gè)函數(shù)值,則我們可以調(diào)用此reflect.Value
值的Call
方法來(lái)調(diào)用此函數(shù)。 每個(gè)Call
方法調(diào)用接受一個(gè)[]reflect.Value
類(lèi)型的參數(shù)(表示傳遞給相應(yīng)函數(shù)調(diào)用的各個(gè)實(shí)參)并返回一個(gè)同類(lèi)型結(jié)果(表示相應(yīng)函數(shù)調(diào)用返回的各個(gè)結(jié)果)。
package main
import "fmt"
import "reflect"
type T struct {
A, b int
}
func (t T) AddSubThenScale(n int) (int, int) {
return n * (t.A + t.b), n * (t.A - t.b)
}
func main() {
t := T{5, 2}
vt := reflect.ValueOf(t)
vm := vt.MethodByName("AddSubThenScale")
results := vm.Call([]reflect.Value{reflect.ValueOf(3)})
fmt.Println(results[0].Int(), results[1].Int()) // 21 9
neg := func(x int) int {
return -x
}
vf := reflect.ValueOf(neg)
fmt.Println(vf.Call(results[:1])[0].Int()) // -21
fmt.Println(vf.Call([]reflect.Value{
vt.FieldByName("A"), // 如果是字段b,則造成恐慌
})[0].Int()) // -5
}
請(qǐng)注意:非導(dǎo)出結(jié)構(gòu)體字段值不能用做反射函數(shù)調(diào)用中的實(shí)參。 如果上例中的vt.FieldByName("A")
被替換為vt.FieldByName("b")
,則將產(chǎn)生一個(gè)恐慌。
下面是一個(gè)使用映射反射值的例子。
package main
import "fmt"
import "reflect"
func main() {
valueOf := reflect.ValueOf
m := map[string]int{"Unix": 1973, "Windows": 1985}
v := valueOf(m)
// 第二個(gè)實(shí)參為Value零值時(shí),表示刪除一個(gè)映射條目。
v.SetMapIndex(valueOf("Windows"), reflect.Value{})
v.SetMapIndex(valueOf("Linux"), valueOf(1991))
for i := v.MapRange(); i.Next(); {
fmt.Println(i.Key(), "\t:", i.Value())
}
}
注意:方法reflect.Value.MapRange
方法是從Go 1.12開(kāi)始才支持的。
下面是一個(gè)使用通道反射值的例子。
package main
import "fmt"
import "reflect"
func main() {
c := make(chan string, 2)
vc := reflect.ValueOf(c)
vc.Send(reflect.ValueOf("C"))
succeeded := vc.TrySend(reflect.ValueOf("Go"))
fmt.Println(succeeded) // true
succeeded = vc.TrySend(reflect.ValueOf("C++"))
fmt.Println(succeeded) // false
fmt.Println(vc.Len(), vc.Cap()) // 2 2
vs, succeeded := vc.TryRecv()
fmt.Println(vs.String(), succeeded) // C true
vs, sentBeforeClosed := vc.Recv()
fmt.Println(vs.String(), sentBeforeClosed) // Go true
vs, succeeded = vc.TryRecv()
fmt.Println(vs.String()) //
fmt.Println(succeeded) // false
}
reflect.Value
類(lèi)型的TrySend
和TryRecv
方法對(duì)應(yīng)著只有一個(gè)case
分支和一個(gè)default
分支的select
流程控制代碼塊。
我們可以使用reflect.Select
函數(shù)在運(yùn)行時(shí)刻來(lái)模擬具有不定case
分支數(shù)量的select
流程控制代碼塊。
package main
import "fmt"
import "reflect"
func main() {
c := make(chan int, 1)
vc := reflect.ValueOf(c)
succeeded := vc.TrySend(reflect.ValueOf(123))
fmt.Println(succeeded, vc.Len(), vc.Cap()) // true 1 1
vSend, vZero := reflect.ValueOf(789), reflect.Value{}
branches := []reflect.SelectCase{
{Dir: reflect.SelectDefault, Chan: vZero, Send: vZero},
{Dir: reflect.SelectRecv, Chan: vc, Send: vZero},
{Dir: reflect.SelectSend, Chan: vc, Send: vSend},
}
selIndex, vRecv, sentBeforeClosed := reflect.Select(branches)
fmt.Println(selIndex) // 1
fmt.Println(sentBeforeClosed) // true
fmt.Println(vRecv.Int()) // 123
vc.Close()
// 再模擬一次select流程控制代碼塊。因?yàn)関c已經(jīng)關(guān)閉了,
// 所以需將最后一個(gè)case分支去除,否則它可能會(huì)造成一個(gè)恐慌。
selIndex, _, sentBeforeClosed = reflect.Select(branches[:2])
fmt.Println(selIndex, sentBeforeClosed) // 1 false
}
一些reflect.Value
值可能表示著不合法的Go值。 這樣的值為reflect.Value
類(lèi)型的零值(即沒(méi)有底層值的reflect.Value
值)。
package main
import "reflect"
import "fmt"
func main() {
var z reflect.Value // 一個(gè)reflect.Value零值
fmt.Println(z) //
v := reflect.ValueOf((*int)(nil)).Elem()
fmt.Println(v) //
fmt.Println(v == z) // true
var i = reflect.ValueOf([]interface{}{nil}).Index(0)
fmt.Println(i) //
fmt.Println(i.Elem()) //
fmt.Println(i.Elem() == z) // true
}
從上面的例子中,我們知道,使用空接口interface{}
值做為中介,一個(gè)Go值可以轉(zhuǎn)換為一個(gè)reflect.Value
值。 逆過(guò)程類(lèi)似,通過(guò)調(diào)用一個(gè)reflect.Value
值的Interface
方法得到一個(gè)interface{}
值,然后將此interface{}
斷言為原來(lái)的Go值。 但是,請(qǐng)注意,調(diào)用一個(gè)代表著非導(dǎo)出字段的reflect.Value
值的Interface
方法將導(dǎo)致一個(gè)恐慌。
package main
import (
"fmt"
"reflect"
"time"
)
func main() {
vx := reflect.ValueOf(123)
vy := reflect.ValueOf("abc")
vz := reflect.ValueOf([]bool{false, true})
vt := reflect.ValueOf(time.Time{})
x := vx.Interface().(int)
y := vy.Interface().(string)
z := vz.Interface().([]bool)
m := vt.MethodByName("IsZero").Interface().(func() bool)
fmt.Println(x, y, z, m()) // 123 abc [false true] true
type T struct {x int}
t := &T{3}
v := reflect.ValueOf(t).Elem().Field(0)
fmt.Println(v) // 3
fmt.Println(v.Interface()) // panic
}
Value.IsZero
方法是Go 1.13中引進(jìn)的,此方法用來(lái)查看一個(gè)值是否為零值。
從Go 1.17開(kāi)始,一個(gè)切片可以被轉(zhuǎn)化為一個(gè)相同元素類(lèi)型的數(shù)組的指針類(lèi)型。 但是如果在這樣的一個(gè)轉(zhuǎn)換中數(shù)組類(lèi)型的長(zhǎng)度過(guò)長(zhǎng),將導(dǎo)致恐慌產(chǎn)生。 因此Go 1.17同時(shí)引入了一個(gè)Value.CanConvert(T Type)
方法,用來(lái)檢查一個(gè)轉(zhuǎn)換是否會(huì)成功(即不會(huì)產(chǎn)生恐慌)。
一個(gè)使用了CanConvert
方法的例子:
package main
import (
"fmt"
"reflect"
)
func main() {
s := reflect.ValueOf([]int{1, 2, 3, 4, 5})
ts := s.Type()
t1 := reflect.TypeOf(&[5]int{})
t2 := reflect.TypeOf(&[6]int{})
fmt.Println(ts.ConvertibleTo(t1)) // true
fmt.Println(ts.ConvertibleTo(t2)) // true
fmt.Println(s.CanConvert(t1)) // true
fmt.Println(s.CanConvert(t2)) // false
}
上面這些例子并未觸及到所有的和reflect.Value
相關(guān)的函數(shù)和方法,請(qǐng)閱讀reflect
標(biāo)準(zhǔn)庫(kù)包的文檔以獲取如何使用這些函數(shù)和方法。 另外請(qǐng)注意Go細(xì)節(jié)101中提到的一些關(guān)于反射的細(xì)節(jié)。
更多建議: