速戰速決 go https://github.com/webabcd/GoSample
作者 webabcd
速戰速決 go - go 高級: 反射(通過反射獲取變量的類型,通過反射獲取變量的值,修改變量的值,獲取結構體成員的值,修改結構體成員的值,調用結構體的方法,通過反射實例化,通過反射調用函數)
示例如下:
advanced/reflect.go
// go 高級 - 反射(通過反射獲取變量的類型,通過反射獲取變量的值,修改變量的值,獲取結構體成員的值,修改結構體成員的值,調用結構體的方法,通過反射實例化,通過反射調用函數)
package advanced
import (
"fmt"
"reflect"
)
func ReflectSample() {
// 通過反射獲取變量的類型
reflect_Sample1()
// 通過反射獲取變量的值,修改變量的值,獲取結構體成員的值,修改結構體成員的值,調用結構體的方法
reflect_Sample2()
// 通過反射實例化,通過反射調用函數
reflect_Sample3()
}
func reflect_Sample1() {
a := myStruc2{salary: 3500, myStruct1: myStruct1{name: "webabcd", age: 40}}
// reflect.TypeOf() - 通過反射獲取指定變量的類型
// Name() - 類型的名稱
// Kind() - 類型的種類
b := reflect.TypeOf(a)
fmt.Println(b, b.Name(), b.Kind()) // advanced.myStruc2 myStruc2 struct
c := &a
d := reflect.TypeOf(c)
// 如果是指針類型,則可以通過 Elem() 獲取指針指向的值的類型
fmt.Println(d.Kind(), d.Elem(), d.Elem().Name(), d.Elem().Kind()) // ptr advanced.myStruc2 myStruc2 struct
// 遍歷結構體的所有成員
for i := 0; i < b.NumField(); i++ {
// 獲取結構體的指定索引的成員
field := b.Field(i)
// 獲取成員的類型,名稱,標籤
fmt.Printf("type:%v, name:%v, tag:%v\n", field.Type, field.Name, field.Tag)
}
// type:float32, name:salary, tag:
// type:advanced.myStruct1, name:myStruct1, tag:
// type:float32, name:SalaryExpected, tag:
// type:*int, name:ptr, tag
// 根據名稱查找結構體的指定成員
field_salary, ok_salary := b.FieldByName("salary")
fmt.Println(field_salary.Type, ok_salary) // float32 true
// 根據索引查找結構體的指定成員(這種方式支持多級查找,本例先找 myStruc2 的索引爲 1 的成員,即 myStruct1,然後再找 myStruct1 的索引爲 0 的成員,即 name)
field_name := b.FieldByIndex([]int{1, 0})
fmt.Println(field_name.Type) // string
}
func reflect_Sample2() {
var a int = 123
// reflect.ValueOf() - 通過反射獲取指定變量的值
// Int(), Float(), String() 等 - 按指定的類型獲取值
// Interface() - 獲取接口,後續可以通過類型斷言的方式獲取值
b := reflect.ValueOf(a)
// 通過反射獲取變量的值
c := b.Int()
// 通過反射獲取變量的值(用斷言的方式)
d, ok := b.Interface().(int)
fmt.Println(c, d, ok) // 123 123 true
// 通過反射修改變量的值(下面這種方式會報錯,因爲傳入的是 a 的值,它是不能被尋址的,即不知道 a 的值保存在哪裏)
// reflect.ValueOf(a).SetInt(456)
// 通過反射修改變量的值(下面這種方式是正確的,因爲傳入的是 a 的指針,它是可以被尋址的,即知道 a 的值保存在哪裏)
reflect.ValueOf(&a).Elem().SetInt(456)
fmt.Println(a) // 456
e := myStruc2{salary: 3500, myStruct1: myStruct1{name: "webabcd", age: 40}, SalaryExpected: 4000}
f := reflect.ValueOf(e)
// 通過反射獲取結構體的指定成員的值(獲取結構體成員的其他方式可以參見上面的 reflect_Sample1() 中的代碼,方法是類似的)
field_salaryExpected := f.FieldByName("SalaryExpected")
fmt.Println(field_salaryExpected.Float()) // 4000
// 如果找不到指定的成員則 IsValid() 爲 false
// 如果值是 nil 則 IsNil() 爲 true
fmt.Println(f.FieldByName("xxx").IsValid(), f.FieldByName("ptr").IsNil()) // false true
// 通過反射修改結構體的指定成員的值
// 下面這種方式會報錯,雖然傳入的是 e 的指針,是可被尋址的,但是修改的成員是不可導出的,即小寫開頭的
// reflect.ValueOf(&e).Elem().FieldByName("salary").SetFloat(5000)
// 下面這種方式是正確的,也就是說傳入的是 e 的指針,是可被尋址的,並且修改的成員是可導出的,即大寫開頭的
reflect.ValueOf(&e).Elem().FieldByName("SalaryExpected").SetFloat(5000)
fmt.Println(e.SalaryExpected) // 5000
// 通過反射調用結構體的方法
// 拿到方法,必須是可導出的,即大寫開頭的
method := reflect.ValueOf(&e).MethodByName("Show")
// 構造參數
args := []reflect.Value{reflect.ValueOf("v1"), reflect.ValueOf("v2")}
// 調用方法並傳參,然後獲取結果
g := method.Call(args)[0]
fmt.Println(g) // name:webabcd, age:40, p1:v1, p2:v2
}
func reflect_Sample3() {
a := 123
// 通過反射獲取指定變量的類型
b := reflect.TypeOf(a)
// 根據反射類型創建類型實例
c := reflect.New(b)
// 通過反射修改變量的值
c.Elem().SetInt(456)
// 通過反射獲取變量的值
fmt.Println(a, c.Elem().Int()) // 123 456
// 通過反射調用函數
// 拿到函數
method := reflect.ValueOf(myFunc1)
// 構造參數
args := []reflect.Value{reflect.ValueOf("v1"), reflect.ValueOf("v2")}
// 調用函數並傳參,然後獲取結果
d := method.Call(args)[0]
fmt.Println(d) // p1:v1, p2:v2
}
type myStruct1 struct {
name string
age int
}
type myStruc2 struct {
salary float32
myStruct1
SalaryExpected float32
ptr *int
}
func (s *myStruc2) Show(p1, p2 string) string {
return fmt.Sprintf("name:%s, age:%d, p1:%s, p2:%s", s.name, s.age, p1, p2)
}
func myFunc1(p1, p2 string) string {
return fmt.Sprintf("p1:%s, p2:%s", p1, p2)
}
速戰速決 go https://github.com/webabcd/GoSample
作者 webabcd