go语言通过反射创建结构体、赋值、并调用对应的操作

时间:2022-09-20 22:03:47

我就废话不多说了,大家还是直接看代码吧~

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main
import (
    "fmt"
    "reflect"
    "testing"
)
type Call struct {
    Num1 int
    Num2 int
}
func (call Call) GetSub(name string){
    fmt.Printf("%v 完成了减法运算,%v - %v = %v \n", name, call.Num1, call.Num2, call.Num1 - call.Num2)
}
func (call *Call) GetSum(name string){
    fmt.Printf("%v 完成了加法运算,%v + %v = %v \n", name, call.Num1, call.Num2, call.Num1 + call.Num2)
}
func TestReflect(t *testing.T) {
    var (
        call *Call
        rValues []reflect.Value
        rValues2 []reflect.Value
    )
    ptrType := reflect.TypeOf(call) //获取call的指针的reflect.Type
    trueType := ptrType.Elem() //获取type的真实类型
    ptrValue := reflect.New(trueType) //返回对象的指针对应的reflect.Value
    call = ptrValue.Interface().(*Call)
    trueValue := ptrValue.Elem() //获取真实的结构体类型
    trueValue.FieldByName("Num1").SetInt(123)//设置对象属性,注意这个一定要是真实的结构类型的reflect.Value才能调用,指针类型reflect.Value的会报错
    //ptrValue.FieldByName("Num2").SetInt(23)
    trueValue.FieldByName("Num2").SetInt(23)
    //rValues = make([]reflect.Value, 0)
    rValues = append(rValues, reflect.ValueOf("xiaopeng"))//调用对应的方法
    fmt.Println(rValues)
    trueValue.MethodByName("GetSub").Call(rValues)
    /*
    fixme 在反射中,指针的方法不可以给实际类型调用,实际类型的方法可以给指针类型调用,因为go语言对这种操作做了封装
    所以下面一句是没问题的
    下下一句会运行时报错
     */
    //ptrValue.MethodByName("GetSub").Call(rValues)
    //trueValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram")))
    ptrValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram")))
    fmt.Println(call)
    
    /*
    fixme 在实际使用中  指针和实体都能相互转换,不会影响调用
    但是指针的方法在方法体内的操作会影响到结构体本身属性
    而实体的方法不会,因为go对于结构体、数组、基本类型都是值传递
     */
    call.GetSub("aaa")
    (*call).GetSub("bbb")
    call.GetSum("ccc")
    (*call).GetSum("ddd")
}

补充:golang 反射 reflect 设置 struct 字段

说明1 reflect.Value区分CanSet和Can not Set

所以, 必须要返回成Can set的reflect.Value

如:

?
1
s := reflect.ValueOf(&t).Elem()

然后就可以happy的设值了, 可是不能随便设值的, 一个通用的方法就是使用Set(v Value)方法,

说明2 将值转成reflect.Value类型

下面的这段代码就是转成Value类型

?
1
sliceValue := reflect.ValueOf([]int{1, 2, 3}) // 这里将slice转成reflect.Value类型

说明3 reflect.ValueOf 参数必须是一个 指针 或 interface Elem()才可以正常调用

func (Value) Elem func (v Value) Elem() Value

Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil.

Elem返回接口v包含的值或指针v指向的值。 如果v的Kind不是Interface或Ptr,它会感到恐慌。 如果v为零,它将返回零值。

实例代码

代码1:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func Destroy(subj interface{}) {
    stype := reflect.ValueOf(subj).Elem()
    field := stype.FieldByName("Status")
    if field.IsValid() {
        field.SetString("Destroyed")
    }
}
 
func TestDestroy(t *testing.T) {
    // Initialize data
    jaeger := Jaeger{Name: "Cherno Alpha", Country: "RU", Status: "Active"}
    kaiju := Kaiju{Alias: "Scissure", Origin: "Sydney", Status: "Unknown"}
    shatterdome := Shatterdome{Location: "Lima"}
 
    // Destroy everything
    Destroy(&jaeger)
    Destroy(&kaiju)
    Destroy(&shatterdome)
 
    // Check the result
    if jaeger.Status != "Destroy" {
        t.Error("jaeger was not destroyed")
    }
    if kaiju.Status != "Destroy" {
        t.Error("kaiju was not destroyed")
    }
}

代码2:

?
1
2
3
4
5
6
7
8
9
10
11
type T struct {
    Age int
    Name string
    Children []int
}
t := T{12, "someone-life", nil}
s := reflect.ValueOf(&t).Elem()
 
s.Field(0).SetInt(123) // 内置常用类型的设值方法
sliceValue := reflect.ValueOf([]int{1, 2, 3}) // 这里将slice转成reflect.Value类型
s.FieldByName("Children").Set(sliceValue)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://www.cnblogs.com/hirampeng/p/11204475.html