Go语言反射(reflect)及应用 (2)

同时使用到reflect.Value.Elem()方法转换成对应保管的值的Value封装,或者持有的指针指向的值的Value封装。

func testElem(b interface{}) { rVal := reflect.ValueOf(b) rVal.Elem().SetInt(20)//Elem()转换指针为所指向的值,相当于用一个变量引用该指针指向的值 } /* func (Value) Elem eg: func (v Value) Elem() Value Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。 如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。*/

image-20201103192118610

实例

需求:使用反射来遍历结构体的字段,调用结构体的方法,修改结构体字段的值,并获取结构体标签的值

package main import ( "fmt" "reflect" ) //使用反射来遍历结构体的字段,调用结构体的方法,修改结构体字段的值,并获取结构体标签的值 //定义结构体 type Student struct { Name string `json:"name"` // 是 ` ` (tab键上的~按键) ,不是 ' ' Sex string `json:"sex"` Age int `json:"age"` Sal float64 `json:"sal"` } func (s Student) GetName() string { //第0个方法 fmt.Println("该结构体Name字段值为:",s.Name) return s.Name } func (s *Student) Set(newName string,newAge int,newSal float64){ //第2个方法 s.Name = newName s.Age = newAge s.Sal = newSal s.Print() } func (s Student) Print() { //第1个方法 fmt.Println("调用 Print 函数输出结构体:",s) } //反射获取结构体字段、方法,并调用 func testReflect(b interface{}) { rVal := reflect.ValueOf(b).Elem() rType := reflect.TypeOf(b).Elem() //判断是否是结构体在进行下一步操作 if rType.Kind() != reflect.Struct{ fmt.Println("该类型不是结构体。所以无法获取字段及其方法。") } //获取字段数量 numField := rVal.NumField() fmt.Printf("该结构体有%d个字段\n",numField) //遍历字段 for i := 0; i < numField; i++ { //获取字段值、标签值 rFieldTag := rType.Field(i).Tag.Get("json") if rFieldTag != "" { fmt.Printf("结构体第 %v 个字段值为:%v ," + "Tag‘json’名为:%v\n",i,rVal.Field(i),rFieldTag) } } //获取方法数量 numMethod := rVal.NumMethod() //用指针可以获取到指针接收的方法 fmt.Printf("该结构体有%d个方法\n",numMethod) //调用方法(方法顺序 按照ACSII码排序) rVal.Method(0).Call(nil) rVal.Method(1).Call(nil) //参数也需要以 Value 的切片 传入 params := make([]reflect.Value ,3) params[0] = reflect.ValueOf("hhhh") params[1] = reflect.ValueOf(28) params[2] = reflect.ValueOf(99.9) rVal.Method(2).Call(params) rVal.Method(1).Call(nil) } func main() { stu := Student{ Name: "莉莉安", Sex: "f", Age: 19, Sal: 98.5, } //调用编写的函数并输出 testReflect(&stu) fmt.Println("主函数输出结构体 Student :",stu) }

image--20201103

上面方法无法通过调用结构体中指针接收的方法,来修改结构体字段,无法获取指针接收的修改方法。

已解决,可选思路如下:

可通过直接获取字段值进行修改。(不够便捷)

用指针类型的reflect.Value可以获取到指针接收的方法(同时还包括值接受者的方法),不转换为指针所指向的值,直接用指针操作即可。

可以识别并使用出指针接收的结构体的所有方法,包括值接收的、指针接收的方法。(前提是原结构体有修改方法)

func (Value) Elem()

Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。

注意:并不是地址,或者指向原值的引用。

结合解决思路,修改结果如下:

package main import ( "fmt" "reflect" ) //使用反射来遍历结构体的字段,调用结构体的方法,修改结构体字段的值,并获取结构体标签的值 //定义结构体 type Student struct { Name string `json:"name"` // 是 ` ` (tab键上的~按键) ,不是 ' ' Sex string `json:"sex"` Age int `json:"age"` Sal float64 `json:"sal"` } func (s Student) GetName() string { //第0个方法 fmt.Println("该结构体Name字段值为:",s.Name) return s.Name } func (s *Student) Set(newName string,newAge int,newSal float64){ //第2个方法 s.Name = newName s.Age = newAge s.Sal = newSal s.Print() } func (s Student) Print() { //第1个方法 fmt.Println("调用 Print 函数输出结构体:",s) } //反射获取结构体字段、方法,并调用 func testReflect(b interface{}) { rVal := reflect.ValueOf(b).Elem() rValI := reflect.ValueOf(b) rType := reflect.TypeOf(b).Elem() //判断是否是结构体在进行下一步操作 if rType.Kind() != reflect.Struct{ fmt.Println("该类型不是结构体。所以无法获取字段及其方法。") } //获取字段数量 numField := rVal.NumField() fmt.Printf("该结构体有%d个字段\n",numField) //遍历字段 for i := 0; i < numField; i++ { //获取字段值、标签值 rFieldTag := rType.Field(i).Tag.Get("json") if rFieldTag != "" { fmt.Printf("结构体第 %v 个字段值为:%v ," + "Tag‘json’名为:%v\n",i,rVal.Field(i),rFieldTag) } } //获取方法数量 numMethod := rValI.NumMethod() //用指针可以获取到指针接收的方法 fmt.Printf("该结构体有%d个方法\n",numMethod) //调用方法(方法顺序 按照ACSII码排序) rVal.Method(0).Call(nil) rVal.Method(1).Call(nil) //参数也需要以 Value 的切片 传入 params := make([]reflect.Value ,3) params[0] = reflect.ValueOf("hhhh") params[1] = reflect.ValueOf(28) params[2] = reflect.ValueOf(99.9) rValI.Method(2).Call(params) rVal.Method(1).Call(nil) } func main() { stu := Student{ Name: "莉莉安", Sex: "f", Age: 19, Sal: 98.5, } //调用编写的函数并输出 testReflect(&stu) fmt.Println("主函数输出结构体 Student :",stu) }

image-20201103175931261

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspgsf.html