網(wǎng)站備案后可以更換域名嗎網(wǎng)絡(luò)營(yíng)銷是學(xué)什么
文章目錄
- 獲取類型和值
- 獲取屬性的類型和值
- 通過(guò)反射修改值
- 獲取方法的名稱和類型
- 調(diào)用方法
- 反射的缺點(diǎn)
獲取類型和值
- 之前講過(guò)接口
nil
不一定等于空接口,因?yàn)橐粋€(gè) interface 底層 由type
+value
構(gòu)成,只有type
和value
都匹配,才能==
reflect.VlaueOf
就是用來(lái)獲取具體的reflect.Value
reflect.TypeOf
用來(lái)獲取具體的reflect.Type
func main() {var (a *Ab interface{})fmt.Println(a)if b == nil {fmt.Println("b is nil")}fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))fmt.Println(reflect.TypeOf(a), reflect.ValueOf(a))b = aif b == nil {fmt.Println("b is nil")} else {fmt.Printf("current b is %v \n", b)fmt.Println("b not eq nil")}fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))
}
上面的代碼說(shuō)明了,剛開(kāi)始的空接口 == nil,后來(lái)的接口為啥不等于 nil,因?yàn)?type變了,雖然value 還是 nil
獲取屬性的類型和值
- 通過(guò)
reflect.Value
或者reflect.Type
的NumField
獲取屬性數(shù)量 - 通過(guò)
reflect.Type
的Field 方法
獲取屬性相關(guān)信息 - 通過(guò)
reflect.Value
的Field 方法
獲取值相關(guān)信息
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func main() {var a AgetType := reflect.TypeOf(a)getValue := reflect.ValueOf(a)fmt.Println("field num", getType.NumField())for i := 0; i < getType.NumField(); i++ {field := getType.Field(i)value := getValue.Field(i)fmt.Println("field name is", field.Name, "field value is", value.Interface())}
}
通過(guò)反射修改值
- 通過(guò)獲取value的反射對(duì)象即可,
reflect.ValueOf
傳入的必須是指針類型,只有原始反射對(duì)象可以進(jìn)行修改,可以通過(guò)reflect.Value
的Elem
方法取得 - 通過(guò)
reflect.Value
的Canset
方法來(lái)判斷是否可以設(shè)置 - 通過(guò)
Set...
系列方法來(lái)設(shè)置具體類型的值
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func main() {a := A{Name: "old name",}valueOfA := reflect.ValueOf(&a).Elem()nameField := valueOfA.Field(0)if nameField.CanSet() {nameField.SetString("new name")} else {fmt.Println("don't set")}fmt.Println("new value", a.Name)
}
- 因?yàn)檎{(diào)用
set...
設(shè)置值,需要知道類型,可以通過(guò)reflect.Type
的kind
方法獲取原始類型- 再通過(guò) switch 去匹配類型來(lái)調(diào)用具體的
set...
方法
- 再通過(guò) switch 去匹配類型來(lái)調(diào)用具體的
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func main() {a := A{Name: "old name",}fmt.Println("old value", a.Name)valueOfA := reflect.ValueOf(&a).Elem()getType := reflect.TypeOf(a)field := getType.Field(0)nameField := valueOfA.Field(0)if nameField.CanSet() {switch field.Type.Kind() {case reflect.String:fmt.Println("string")nameField.SetString("new value")}} else {fmt.Println("don't set")}fmt.Println("new value", a.Name)
}
獲取方法的名稱和類型
- 先通過(guò)
reflect.Type
的NumMethod
方法獲取方法數(shù)量 - 在通過(guò)
reflect.Type
的Method
方法獲取到具體的方法信息reflect.Method
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func (receiver *A) SetName(name string) {receiver.Name = name
}func (receiver *A) SetAge(age int) {receiver.Age = age
}func main() {var a A//有方法是依賴指針的所以需要傳指針getType := reflect.TypeOf(&a)num := getType.NumMethod()for i := 0; i < num; i++ {method := getType.Method(i)fmt.Println("method name:", method.Name, "method type:", method.Type)}
}
調(diào)用方法
- 通過(guò)
reflect.Method
的Call
方法即可調(diào)用反射對(duì)象的方法- Call 中 接收的參數(shù)為
reflect.Value
的切片 - 如果反射對(duì)象的方法不需要參數(shù),傳一個(gè)
reflect.Value
的空切片即可 - 如果反射對(duì)象需參數(shù),那么需要由反射對(duì)像參數(shù)的
reflect.Value
組成切片,傳入Call
完成調(diào)用
- Call 中 接收的參數(shù)為
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}type Body struct {Like stringDesc string
}func (a A) Pr() {fmt.Println("A pr")
}func (a A) Talk(b Body) {fmt.Printf("Like:%s,Desc:%s", b.Like, b.Desc)
}func main() {var a AgetType := reflect.ValueOf(a)pr := getType.Method(0)//不需要參數(shù)pr.Call([]reflect.Value{})b := Body{Like: "i'm like",Desc: "i'm desc",}talk := getType.Method(1)//Talk 需要傳入 Body struct, 所以反射調(diào)用,需要傳入 由 Body的 reflect.Value 組成切片參數(shù)talk.Call([]reflect.Value{reflect.ValueOf(b),})
}
反射的缺點(diǎn)
- 反射慢
- 不管什么編程語(yǔ)言,反射都慢
- 反射實(shí)現(xiàn)里有對(duì)
reflect.kind
大量的枚舉 + 類型轉(zhuǎn)換 等操作 reflect.Value
不能復(fù)用,每次都是返回一個(gè)新的值,其中 typ 還是指針類型,涉及對(duì)指針的頻繁分配,GC