反射(Reflection)
為什么需要反射
有時(shí)候需要知道未知類(lèi)型的類(lèi)型表達(dá)方式, 有時(shí)候需要獲取類(lèi)型信息, 進(jìn)行判斷進(jìn)行不同的處理
reflect.Type
和 reflect.Value
reflect包中兩個(gè)重要的類(lèi)型.
-
reflect.Type
是一個(gè)接口, 表示一個(gè)Go類(lèi)型 - 可由
reflect.TypeOf()
以reflect.Type
的類(lèi)型返回某個(gè)interface{}
的動(dòng)態(tài)類(lèi)型信息
t := reflect.TypeOf(3) // t: a reflect.Type
fmt.Println(t.String()) // "int"
// reflect.Type滿(mǎn)足fmt.Stringer接口
fmt.Println(t) // "int"
var w io.Writer = os.Stdout
fmt.Println(reflect.TypeOf(w)) // "*os.File" not io.Writer
-
reflect.Value
可裝載任意類(lèi)型的值, 滿(mǎn)足Stringer
接口,reflect.ValueOf()
返回一個(gè)Value
v := reflect.ValueOf(3) // a reflect.Value
fmt.Println(v) // "3"
fmt.Printf("%v\n", v) // "3"
fmt.Println(v.String()) // NOTE: "<int Value>"
-
Value
的Type
方法返回reflect.Type
t := v.Type() // a reflect.Type
fmt.Println(t.String()) // "int"
-
reflect.ValueOf()
的逆操作是reflect.Value.Interface()
: 返回一個(gè)interface{} 岳锁,其值是與Value相同的具體值
v := reflect.ValueOf(3) // a reflect.Value
x := v.Interface() // an interface{}
i := x.(int) // an int
fmt.Printf("%d\n", i) // "3"
- 跟interface{}不同的是無(wú)需用type斷言知道動(dòng)態(tài)類(lèi)型, 比如以下
format.Any
使用Kind
方法,得到有限的Kind進(jìn)行處理
package format
import (
"reflect"
"strconv"
)
// Any formats any value as a string.
func Any(value interface{}) string {
return formatAtom(reflect.ValueOf(value))
}
// formatAtom formats a value without inspecting its internal structure.
func formatAtom(v reflect.Value) string {
switch v.Kind() {
case reflect.Invalid:
return "invalid"
case reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64:
return strconv.FormatInt(v.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(v.Uint(), 10)
// ...floating-point and complex cases omitted for brevity...
case reflect.Bool:
return strconv.FormatBool(v.Bool())
case reflect.String:
return strconv.Quote(v.String())
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
return v.Type().String() + " 0x" +
strconv.FormatUint(uint64(v.Pointer()), 16)
default: // reflect.Array, reflect.Struct, reflect.Interface
return v.Type().String() + " value"
}
}
display, 一個(gè)遞歸值打印器
聚合類(lèi)型只打印了類(lèi)型, 引用類(lèi)型打印地址, 需要進(jìn)一步精細(xì)化處理
Display("e", expr)
func Display(name string, x interface{}) {
fmt.Printf("Display %s (%T):\n", name, x)
display(name, reflect.ValueOf(x))
}
func display(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++ {
display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
}
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
display(fieldPath, v.Field(i))
}
case reflect.Map:
for _, key := range v.MapKeys() {
display(fmt.Sprintf("%s[%s]", path,
formatAtom(key)), v.MapIndex(key))
}
case reflect.Ptr:
if v.IsNil() {
fmt.Printf("%s = nil\n", path)
} else {
display(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())
display(path+".value", v.Elem())
}
default: // basic types, channels, funcs
fmt.Printf("%s = %s\n", path, formatAtom(v))
}
}
-
Slice
和Array
:Len()
返回?cái)?shù)組元素個(gè)數(shù),Index()
返回元素的reflect.Value
,越界會(huì)panic -
Struct
:NumField
返回結(jié)構(gòu)體字段個(gè)數(shù),Field(i)
返回第i字段reflect.Value
形式的值 -
Map
:MapKeys
返回一個(gè)reflect.Value
類(lèi)型的slice, 對(duì)應(yīng)map的keys, 遍歷仍然是隨機(jī)的,MapIndex(key)
返回key對(duì)應(yīng)的值Value - 指針:
Elem
返回指針指向的變量, 依然是reflect.Value
類(lèi)型, nil也是安全的, type此時(shí)為Invalid
類(lèi)型, 可用IsNil()
事先判斷 - 接口: 先
IsNil
判斷, 然后用v.Elem()獲取動(dòng)態(tài)值
通過(guò)reflect.Value修改值
有一些reflect.Values是可取地址的, 這種是可以設(shè)置其值的
x := 2 // value type variable?
a := reflect.ValueOf(2) // 2 int no
b := reflect.ValueOf(x) // 2 int no
c := reflect.ValueOf(&x) // &x *int no
d := c.Elem() // 2 int yes (x)
fmt.Println(d.CanAddr()) // "true"
px := d.Addr().Interface().(*int) // px := &x
*px = 3 // x = 3
d.Set(reflect.ValueOf(4))
- CanAddr方法來(lái)判斷其是否可以被取地址
- 通過(guò)調(diào)用可取地址的reflect.Value的reflect.Value.Set方法來(lái)更新
- Set方法將在運(yùn)行時(shí)執(zhí)行和編譯時(shí)進(jìn)行類(lèi)似的可賦值性約束的檢查
- Set方法:SetInt橡伞、SetUint、SetString和SetFloat等
- 反射機(jī)制不能修改未導(dǎo)出成員
- CanSet是用于檢查對(duì)應(yīng)的reflect.Value是否是可取地址并可被修改
- reflect.Zero函數(shù)將變量v設(shè)置為零值
x := 1
rx := reflect.ValueOf(&x).Elem()
rx.SetInt(2) // OK, x = 2
rx.Set(reflect.ValueOf(3)) // OK, x = 3
rx.SetString("hello") // panic: string is not assignable to int
rx.Set(reflect.ValueOf("hello")) // panic: string is not assignable to int
var y interface{}
ry := reflect.ValueOf(&y).Elem()
ry.SetInt(2) // panic: SetInt called on interface Value
ry.Set(reflect.ValueOf(3)) // OK, y = int(3)
ry.SetString("hello") // panic: SetString called on interface Value
ry.Set(reflect.ValueOf("hello")) // OK, y = "hello"
獲取結(jié)構(gòu)體字段標(biāo)簽
reflect.Type
的Field()
將返回一個(gè)reflect.StructField
, 含有每個(gè)成員的名字召嘶、類(lèi)型和可選的成員標(biāo)簽等信息蜻懦。其中成員標(biāo)簽信息對(duì)應(yīng)reflect.StructTag
類(lèi)型的字符串间螟,并且提供了Get
方法用于解析和根據(jù)特定key提取的子串
// Unpack populates the fields of the struct pointed to by ptr
// from the HTTP request parameters in req.
func Unpack(req *http.Request, ptr interface{}) error {
if err := req.ParseForm(); err != nil {
return err
}
// Build map of fields keyed by effective name.
fields := make(map[string]reflect.Value)
v := reflect.ValueOf(ptr).Elem() // the struct variable
for i := 0; i < v.NumField(); i++ {
fieldInfo := v.Type().Field(i) // a reflect.StructField
tag := fieldInfo.Tag // a reflect.StructTag
name := tag.Get("http")
if name == "" {
name = strings.ToLower(fieldInfo.Name)
}
fields[name] = v.Field(i)
}
// Update struct field for each parameter in the request.
for name, values := range req.Form {
f := fields[name]
if !f.IsValid() {
continue // ignore unrecognized HTTP parameters
}
for _, value := range values {
if f.Kind() == reflect.Slice {
elem := reflect.New(f.Type().Elem()).Elem()
if err := populate(elem, value); err != nil {
return fmt.Errorf("%s: %v", name, err)
}
f.Set(reflect.Append(f, elem))
} else {
if err := populate(f, value); err != nil {
return fmt.Errorf("%s: %v", name, err)
}
}
}
}
return nil
}
func populate(v reflect.Value, value string) error {
switch v.Kind() {
case reflect.String:
v.SetString(value)
case reflect.Int:
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
v.SetInt(i)
case reflect.Bool:
b, err := strconv.ParseBool(value)
if err != nil {
return err
}
v.SetBool(b)
default:
return fmt.Errorf("unsupported kind %s", v.Type())
}
return nil
}
顯示一個(gè)類(lèi)型的方法集
-
reflect.Type
和reflect.Value
都提供了Method
方法 - 每次
t.Method(i)
調(diào)用返回reflect.Method的實(shí)例 - 每次
v.Method(i)
調(diào)用都返回一個(gè)reflect.Value
以表示方法值 - 使用reflect.Value.Call方法調(diào)用一個(gè)Func類(lèi)型的Value
// Print prints the method set of the value x.
func Print(x interface{}) {
v := reflect.ValueOf(x)
t := v.Type()
fmt.Printf("type %s\n", t)
for i := 0; i < v.NumMethod(); i++ {
methType := v.Method(i).Type()
fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
strings.TrimPrefix(methType.String(), "func"))
}
}
深度相等判斷
reflect.DeepEqual
可以對(duì)兩個(gè)值進(jìn)行深度相等判斷, 使用基礎(chǔ)類(lèi)型的==
判斷, 會(huì)遞歸復(fù)合類(lèi)型
func TestSplit(t *testing.T) {
got := strings.Split("a:b:c", ":")
want := []string{"a", "b", "c"};
if !reflect.DeepEqual(got, want) { /* ... */ }
}
使用反射的忠告
- 基于反射的代碼脆弱, 運(yùn)行時(shí)才會(huì)拋panic
- 不能做靜態(tài)類(lèi)型檢查, 太多則可能難以理解
- 運(yùn)行速度慢一到兩個(gè)數(shù)量級(jí), 測(cè)試適合使用, 性能關(guān)鍵路徑避免使用
- 若非真正需要, 請(qǐng)不要使用reflect