參考 :http://c.biancheng.net/view/4407.html
反射是指在程序運行期對程序本身進行訪問和修改的能力篓像,程序在編譯時變量被轉換為內存地址阀趴,變量名不會被編譯器寫入到可執(zhí)行部分,在運行程序時程序無法獲取自身的信息
舉個例子說明啥是反射呢袒啼?
就是比如 python getattr 函數(shù)類似茄蚯,通過字段屬性名 拿到字段對應的值。
或者 a = "xxx"
js 里面 map.xxx 也可以 map[a]
go 里面什么情況用反射
我現(xiàn)在想通過字段名的 字符串變量运翼,獲取結構體對應的字段值岁钓, 貌似只能用 xxx_struct.xxx 而不能像 js 里面那樣 xxx_struct["xxx"] , 那就只能用 反射了激涤。
go 怎么實現(xiàn) 反射
使用 內置包 reflect
reflect 定義了兩個重要的類型 Type 和 Value 任意接口值在反射中都可以理解為由 reflect.Type 和 reflect.Value 兩部分組成
reflect 反射 練習
-
反射獲取信息 reflect.TypeOf
返回的是 reflect.Type 類型
reflect.Type 對象可以訪問任意值的類型信息, 有兩個重要的 函數(shù)累提, Name() 返回Type 和 Kind() 返回 Kind 種類
reflect.TypeOf (xxx interface{}).Name() 返回定義
int尘喝、string、bool斋陪、float32 以及 使用 type 定義的類型名
reflect.TypeOf (xxx interface{}).Kind() 返回定義
int朽褪、string、bool无虚、float32 struct func 這種原始的類型名
普通類型獲取反射類型
type A struct{}
var a A
reflect.TypeOf(a).Name() // A
reflect.TypeOf( a ).Kind() // struct
指針類型獲取 反射類型
reflect.TypeOf(a).Elem().Name()/Kind()
獲取結構體的成員類型信息
(只是信息缔赠,并不能取到對應的值)
針對結構體,即如果 反射的 Kind() 返回的 是 struct ,那可以通過一下函數(shù)友题,獲取它成員的信息
- reflect.TypeOf (xxx interface{}).NumField()
返回結構體成員字段數(shù)量
NumField() int
- reflect.TypeOf (xxx interface{}).Field()
返回索引對應的結構體字段的信息
Field(i int) StructField
- reflect.TypeOf (xxx interface{}).FieldByName()
返回字符串對應的結構體字段的信息
FieldByName(name string) (StructField, bool)
- reflect.TypeOf (xxx interface{}).FieldByIndex()
多層成員訪問時嗤堰,根據(jù) []int 提供的每個結構體的字段索引,返回字段的信息
FieldByIndex(index []int) StructField
- reflect.TypeOf (xxx interface{}).FieldByNameFunc()
根據(jù)匹配函數(shù)匹配需要的字段
FieldByNameFunc(match func(string) bool) (StructField,bool)
說明一下度宦, 上面函數(shù)返回為 StructField 對象(Field, FieldByName, FieldByIndex, FieldByNameFunc )踢匣,StructField 對象具有以下屬性
StructField 對象結構體字段
type StructField struct {
Name string // 字段名
PkgPath string // 字段路徑
Type Type // 字段反射類型對象, 字段本身的反射類型對象,類型為
// reflect.Type戈抄,可以進一步獲取字段的類型信息, 像遞歸了
Tag StructTag // 字段的結構體標簽
Offset uintptr // 字段在結構體中的相對偏移
Index []int // Type.FieldByIndex中的返回的索引值
Anonymous bool // 是否為匿名字段
}
舉個例子說明獲取結構體的字段信息
func main(){
type girl struct{
name string `json:"nikname"`
age int
high int
}
var a = girl{"小蓮", 28, 165}
reflect_type := reflect.TypeOf(a)
fmt.Println( "Name():", reflect_type.Name(), "kind():", reflect_type.Kind())
kind := reflect_type.Kind()
if kind == reflect.Struct{
fmt.Printf("是結構體\n")
// 遍歷獲取名稱离唬, Field 通常和 NumField 以前搭配使用, 遍歷結構體划鸽, 這
// 里僅獲取結構體成員信息输莺,并沒有獲取值
for i := 0; i < reflect_type.NumField(); i++{
fmt.Printf("index: %v, file name: %v\n", i, reflect_type.Field(i).Name)
}
// 通過字段名字獲取字段信息.
if info, ok := reflect_type.FieldByName("name"); ok{
fmt.Printf("name is :%v, tag is: %v \n", info.Name, info.Tag.Get("json"))
}
}
}
>
Name(): girl kind(): struct
是結構體
index: 0, file name: name
index: 1, file name: age
index: 2, file name: high
name is :name, tag is: nikname
上面提到了 結構體的反射對象 StructField 里面有個 Struct Tag, 我們來看看這個 TAG 里面是神馬東西把:
字段的額外信息漾稀, 這些信息都是靜態(tài)的模闲,無須實例化結構體,可以通過反射獲取到
書寫結構:
`key1:"value1" key2:"value2"`
結構體標簽由一個或多個鍵值對組成崭捍;鍵與值使用冒號分隔尸折,值用雙引號括起來;鍵值對之間使用一個空格分隔殷蛇, 這個格式很嚴格实夹,有地方多一個空格都不會是期望的結果
取出 key 對應值:
reflect_type.FieldByName("xx").Tag.Get("json")
判斷 key 是不是存在:
Lookup(key)
func (tag StructTag) Lookup(key string) (value string, ok bool)
reflect_type.FieldByName("xx").Tag.Lookup("json")
-
反射獲取值 reflect.ValueOf
普通類型獲取反射類型
value := reflect.ValueOf(rawValue)
返回 reflect.Value 類型
reflect.Value 是一些反射操作的重要類型橄浓,如反射調用函數(shù)
reflect.Value 對象所具有的方法
- 將值以 interface{} 類型返回
Interface() interface {}
- 將值以 int 類型返回(所有有符號整型均可以此方式返回)
Int() int64
- 值以 uint 類型返回(所有無符號整型均可以此方式返回)
Uint() uint64
- 值以雙精度(float64)類型返回,所有浮點數(shù)(float32亮航、float64)均可以此方式返回
Float() float64
- 將值以 bool 類型返回
Bool() bool
- 將值以字節(jié)數(shù)組 []bytes 類型返回
Bytes() []bytes
- 將值以字符串類型返回
String() string
結構體反射獲取值
方法如下荸实, 和獲取信息的一樣,子不過缴淋,獲取的 是 Value 對象准给,不是 StructField 對象
- Field(i int) Value
- NumField() int
- FieldByName(name string) Value
- FieldByIndex(index []int) Value
- FieldByNameFunc(match func(string) bool) Value
關于 值類型的方法參考:
https://golang.google.cn/pkg/reflect/#Value
反射修改值
reflect.Value 修改值得方法(和 獲取值是對應的):
Setlnt(x int64) SetUint(x uint64) SetFloat(x float64) SetBool(x bool)
SetBytes(x []byte SetString(x string)
可以被修改的條件:
傳入地址
var a int = 1024
valueOfA := reflect.ValueOf(&a)
valueOfA = valueOfA.Elem()
valueOfA.SetInt(1)
結構體成員中,字段可導出
type dog struct {
LegCount int
}
valueOfDog := reflect.ValueOf(&dog{})
valueOfDog = valueOfDog.Elem()
vLegCount := valueOfDog.FieldByName("LegCount")
vLegCount.SetInt(4)