通过反射修改结构体字段值
代码
需要转入 指针 类型, 并调用 Elem() 方法 转化, 然后调用 SetXxx() 方法修改属性值
// 运用反射 修改 结构体 字段 案例
// 反射可以用来开发底层框架
package main
import (
"encoding/json"
"fmt"
"reflect"
)
// 这就需要通过 tag 来转化大写的字段,同时又保证内部调用的时候可以暴露变量
// 反射原理
type People struct {
Name string `json:"name01"`
Age int `json:"age"`
Height float64 `json:"height"`
Action string
}
// 给结构体绑定方法
func (p People) Print() {
fmt.Println("print() = ", p)
}
func (p People) GetSum(n1, n2 int) int {
return n1 + n2
}
func (p People) Set(name string, age int, height float64, action string) {
p.Name = name
p.Age = age
p.Height = height
p.Action = action
}
func TestStruct(a interface{}) {
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct {
fmt.Println("except struct, but its not")
return
}
// 结构体字段
// 字段的顺序跟定义结构体时字段的先后顺序一致
num := val.Elem().NumField()
fmt.Printf("struct has %v fields \n", num)
for i := 0; i < num; i++ {
fmt.Printf("第%v个字段:%v \n", i+1, val.Elem().Field(i))
tagVal := typ.Elem().Field(i).Tag.Get("json")
if tagVal != "" {
fmt.Printf("第%v个字段, tag : %v \n", i+1, tagVal)
}
}
// 结构体方法
numOfMethod := val.NumMethod()
fmt.Printf("struct 有 %v 个方法 \n", numOfMethod)
// 内部方法名顺序,按照函数名的 ascii 码排序的
val.Method(1).Call(nil)
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Method(0).Call(params)
fmt.Println("res = ", res[0].Int())
// 修改结构体字段值
val.Elem().Field(1).SetInt(20)
}
func main() {
// struct
p1 := People{
Name: "tom",
Age: 18,
Height: 178.5,
Action: "衣食住行",
}
// 序列化就是通过反射机制获取到 tag 值
p1Str, err := json.Marshal(p1)
if err != nil {
fmt.Println("序列化失败", err)
} else {
fmt.Println("p1Str = ", string(p1Str))
}
// 传入指针类型, 调用 Elem() 和 SetXxx()
TestStruct(&p1)
fmt.Println("p1 = ", p1)
}
/*
print() = {tom 18 178.5 衣食住行}
p1 = {tom 20 178.5 衣食住行}
*/
使用反射机制,编写函数适配器,桥连接
代码
// reflect_test.go 单元测试
// 使用反射机制,编写适配器函数 桥连接
package test
import (
"reflect"
"testing"
)
// type User struct {
// UserId string
// Name string
// }
func TestReflectFunc(t *testing.T) {
// 定义两个不同参数的函数
call1 := func(v1 int, v2 int) {
t.Log(v1, v2)
}
call2 := func(v1 int, v2 int, s string) {
t.Log(v1, v2, s)
}
var (
function reflect.Value
inValue []reflect.Value
n int
)
bridge := func(call interface{}, args ...interface{}) {
n = len(args)
inValue = make([]reflect.Value, n)
for i := 0; i < n; i++ {
inValue[i] = reflect.ValueOf(args[i])
}
function = reflect.ValueOf(call)
// len(in) == 3, v.Call(in)代表调用v(in[0], in[1], in[2])
function.Call(inValue)
}
bridge(call1, 1, 2)
bridge(call2, 1, 2, "test2")
}
/*
go test -v
=== RUN TestReflectFunc
reflect_test.go:17: 1 2
reflect_test.go:20: 1 2 test2
--- PASS: TestReflectFunc (0.00s)
PASS
ok go_code/reflect/reflect04/main 0.545s
*/
通过反射创建并操作结构体
代码
// reflect_test.go 测试用例
// 通过反射创建并操作结构体
package test
import (
"reflect"
"testing"
)
type User struct {
UserId string
Name string
}
func TestReflectStructPtr(t *testing.T) {
var (
model *User
st reflect.Type
elem reflect.Value
)
// 获取 *User 类型
st = reflect.TypeOf(model)
t.Log("reflect.TypeOf = ", st.Kind().String()) // ptr
// st 真正指向的类型
st = st.Elem()
t.Log("reflect.TypeOf.Elem = ", st.Kind().String()) // struct
// New 返回一个 Value 类型的值,该值持有一个指向类型为 typ 的新申请的零值的指针
elem = reflect.New(st)
t.Log("reflect.New", elem.Kind().String()) // ptr
t.Log("reflect.New.Elem", elem.Elem().Kind().String()) // struct
// model 就是创建的 User 结构体变量(实例)
// 类型断言
model = elem.Interface().(*User) // model 是 *User, 它的指向跟elem 是一样的
elem = elem.Elem() // 取得 elem 指向的值
// 结构体赋值
elem.FieldByName("UserId").SetString("123456")
elem.FieldByName("Name").SetString("xiao")
t.Log("model model.Name", model, model.Name)
}
/*
go test -v
=== RUN TestReflectStructPtr
reflect_test.go:24: reflect.TypeOf = ptr
reflect_test.go:27: reflect.TypeOf.Elem = struct
reflect_test.go:30: reflect.New ptr
reflect_test.go:31: reflect.New.Elem struct
reflect_test.go:39: model model.Name &{123456 xiao} xiao
--- PASS: TestReflectStructPtr (0.00s)
PASS
ok go_code/reflect/reflect05/test 0.740s
*/