轉載自: Go Reflect
最近在看一些go語言標準庫以及第三方庫的源碼時挪略,發(fā)現(xiàn)go的reflect被大量使用甫匹,雖然反射的機制大多數(shù)語言都支持箕般,但好像都沒有go一樣這么依賴反射的特性瘤泪。個人覺得键痛,reflect使用如此頻繁的一個重要原因離不開go的另一個特性,空接口interface{},reflect配合空接口羊精,讓原本是靜態(tài)類型的go具備了很多動態(tài)類型語言的特征斯够。 另外,雖然反射大大增加了go語言的靈活性喧锦,但要完全掌握它的原理和使用也還是有一點難度的读规。
go的reflect庫有兩個重要的類型:
reflect.Type
reflect.Value
Type,Value分別對應對象的類型和值數(shù)據(jù)
還有兩個重要的函數(shù):
reflect.TypeOf(i interface{}) Type
reflect.TypeOf()返回值的類型就是reflect.Type。
reflect.ValueOf(i interface{}) Value
reflect.ValueIOf()返回值的類型就是reflect.Value
reflect.Type
reflect.TypeOf(i interface{}) Type
因為reflect.Typeof的參數(shù)是空接口類型燃少,因此可以接收任意類型的數(shù)據(jù)束亏。 TypeOf()的返回值是這個接口類型對應的reflect.Type對象。通過Type提供的一些方法阵具,就可以獲得這個接口實際的靜態(tài)類型枪汪。
import (
"fmt"
"reflect"
)
type Foo struct {
X string
Y int
}
func main() {
var i int = 123
var f float32 = 1.23
var l []string = []string{"a", "b", "c"}
fmt.Println(reflect.TypeOf(i)) //int
fmt.Println(reflect.TypeOf(f)) //float32
fmt.Println(reflect.TypeOf(l)) //[]string
var foo Foo
fmt.Println(reflect.TypeOf(foo)) //main.Foo
}
查看reflect包的源代碼可以看到,reflect.Type的定義如下:
type Type interface {
Align() int
FieldAlign() int
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Name() string
String() string
Elem() Type
Field(i int) StructField
FieldByName(name string) (StructField, bool)
Len() int
.....
}
可見reflect.Type是一個接口類型的對象怔昨,這個接口包含了很多方法雀久,像Name(),Field(),Method()等,下面再通過實例來了解幾個比較重要的方法趁舀。
type Foo struct {
X string
Y int
}
func (f Foo) do() {
fmt.Printf("X is: %s, Y is: %d", f.X, f.Y)
}
func main() {
var s string = "abc"
fmt.Println(reflect.TypeOf(s).String()) //string
fmt.Println(reflect.TypeOf(s).Name()) //string
var f Foo
typ := reflect.TypeOf(f)
fmt.Println(typ.String()) //main.Foo
fmt.Println(typ.Name()) //Foo 赖捌,返回結構體的名字
}
上面的例子可見,通過Type.String(),Type.Name()方法就可以獲得接口對應的靜態(tài)類型矮烹。 下面幾個方法越庇,顯示了Type的更多功能,特別是對于結構體對象而言奉狈。
上面的例子可見卤唉,通過Type.String(),Type.Name()方法就可以獲得接口對應的靜態(tài)類型。 下面幾個方法仁期,顯示了Type的更多功能桑驱,特別是對于結構體對象而言竭恬。
Type的Field是一個StructFiled對象:
type StructField struct {
Name string
PkgPath string
Type Type // field type
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
}
Method相關的方法
var f Foo
typ := reflect.TypeOf(f)
fmt.Println(typ.NumMethod()) //1, Foo 方法的個數(shù)
m := typ.Method(0)
fmt.Println(m.Name) //do
fmt.Println(m.Type) //func(main.Foo)
fmt.Println(m.Func) //<func(main.Foo) Value>, 這個返回的是reflect.Value對象熬的,后面再講
Kind
Kind方法Type和Value都有痊硕,它返回的是對象的基本類型,例如int,bool,slice等押框,而不是靜態(tài)類型岔绸。
var f = Foo{}
typ := reflect.TypeOf(f)
fmt.Println(typ) //main.Foo
fmt.Println(typ.Kind()) //struct
var f2 = &Foo{}
typ2 := reflect.TypeOf(f2)
fmt.Println(typ2) //*main.Foo
fmt.Println(typ2.Kind()) //ptr
kind()的返回值如下:
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
reflect.Value
reflect.ValueOf(i interface{}) Value
reflect.ValueOf()的返回值類型為reflect.Value,它實現(xiàn)了interface{}參數(shù)到reflect.Value的反射
type Foo struct {
X string
Y int
}
func (f Foo) do() {
fmt.Printf("X is: %s, Y is: %d", f.X, f.Y)
}
func main() {
var i int = 123
var f = Foo{"abc", 123}
var s = "abc"
fmt.Println(reflect.ValueOf(i)) //<int Value>
fmt.Println(reflect.ValueOf(f)) //<main.Foo Value>
fmt.Println(reflect.ValueOf(s)) //abc
//Value.String()方法對string類型的數(shù)據(jù)做了特殊處理,會直接返回字符串的值橡伞。
//其它類型對象返回的格式都是"<Type% Value>"
}
reflact.Value對象可以通過調用Interface()方法盒揉,再反射回interface{}對象
reflect.ValueOf() Interface()
interface{} ---------------------> reflect.Value -------------------> interface{}
var i int = 123
fmt.Println(reflect.Valueof(i).Interface()) //123
var f = Foo{"abc", 123}
fmt.Println(f) //{abc 123}
fmt.Println(reflect.ValueOf(f).Interface() == f) //true
fmt.Println(reflect.ValueOf(f).Interface()) //{abc 123}
Value的Field方法
和Type的Filed方法不一樣,Type.Field()返回的是StructFiled對象兑徘,有Name,Type等屬性刚盈,Value.Field()返回的還是一個Value對象。
var foo = Foo{"abc", 123}
val := reflect.ValueOf(foo)
fmt.Println(val.FieldByName("y")) //<int Value> interface.Value對象
typ := reflect.Typeof(foo)
fmt.Println(typ.FieldByName("y")) //{ <nil> 0 [] false} false StructField對象
func main() {
var f = Foo{"abc", 123}
rv := reflect.ValueOf(f)
rt := reflect.TypeOf(f)
for i := 0; i < rv.NumField(); i++ {
fv := rv.Field(i)
ft := rt.Field(i)
fmt.Printf("%s type is :%s ,value is %v\n", ft.Name, fv.Type(), fv.Interface())
}
}
//X type is :string ,value is abc
//Y type is :int ,value is 123
設置Value的值
要設置reflect.Value的值還頗費周折道媚,不能直接對Value進行賦值操作
var s = "abc"
fv := reflect.ValueOf(s)
fmt.Println(fv.CanSet()) //false
// fv.SetString("edf") //panic
fv2 := reflect.ValueOf(&s)
fmt.Println(fv2.CanSet()) //false
// fv2.SetString("edf") //panic
relect.Value是字符s的一個反射對象,是不能直接對它進行賦值操作的翘县。 要對s進行賦值最域,需要先拿到s的指針對應的reflect.Value,然后通過Value.Elem()再對應到s,然后才能賦值操作锈麸。 這個地方是相當拗口啊:(
func main() {
var i int = 123
fv := reflect.ValueOf(i)
fe := reflect.ValueOf(&i).Elem() //必須是指針的Value才能調用Elem
fmt.Println(fe) //<int Value>
fmt.Println(fv) //<int Value>
fmt.Println(fv == fe) //false
fmt.Println(fe.CanSet()) //true
fe.SetInt(456)
fmt.Println(i) //456
}
Method
這個是reflect一個比較經(jīng)典的使用場景镀脂,在知道對象方法名的情況下,調用對象的方法忘伞。
type Foo struct {
X string
Y int
}
func (f Foo) Do() {
fmt.Printf("X is: %s, Y is: %d\n", f.X, f.Y)
}
func main() {
var foo = &Foo{"abc", 123}
reflect.ValueOf(foo).MethodByName("Do").Call([]reflect.Value{})
}
//方法名Do必須是大寫的薄翅,否則會拋異常
reflect整體不是很好理解,如果要進一步掌握如何使用氓奈,以及在什么場景下用翘魄,建議看一些開源庫的代碼,來理解reflect的使用舀奶。參考:
web.go