zoukankan      html  css  js  c++  java
  • (四十五)golang--反射

    反射基本介绍:

    (1)反射可以在运行时动态获取变量的各种信息,比如变量的类型、类别;

    (2)如果是结构体变量,还可以获取结构体本身的信息(包括结构体字段、方法);

    (3)通过反射,可以修改变量的值,可以调用关联的方法;

    (4)使用反射,需要Import "reflect:"

    变量--空接口--relect.Value之间的转换关系:

    int变量与反射变量之间的转换:

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    func reflectTest(b interface{}) {
        rTyp := reflect.TypeOf(b)
        fmt.Printf("rTyp=%v,type=%T
    ", rTyp, rTyp)
        rVal := reflect.ValueOf(b)
        fmt.Printf("rVal=%v,type=%T
    ", rVal, rVal)
    
        iVal := rVal.Interface()
        num2 := iVal.(int)
        fmt.Printf("num2=%v,type=%T
    ", num2, num2)
    }
    
    func main() {
        var num int = 100
        reflectTest(num)
    }

     结构体变量与反射变量之间的转换:

    type student struct {
        name string
        age  int
    }
    
    func reflectTest02(b interface{}) {
        rTyp := reflect.TypeOf(b)
        fmt.Printf("rTyp=%v,type=%T
    ", rTyp, rTyp)
        rVal := reflect.ValueOf(b)
        fmt.Printf("rVal=%v,type=%T
    ", rVal, rVal)
    
        iVal := rVal.Interface()
        stu2 := iVal.(student)
        fmt.Printf("stu2=%v,type=%T
    ", stu2, stu2)
        fmt.Printf("name:%v,age:%v", stu2.name, stu2.age)
    }
    
    func main() {
        stu := student{
            name: "tom",
            age:  20,
        }
        reflectTest02(stu)
    }

    反射注意事项和使用细节:
    (1)reflect.Vale.Kind,获取变量的类别,返回的是一个常量;

    (2)Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的;

    比如:var num int=10的type是Int,Kind也是int

         var stu Student, stu的rype是pkg1.Student,Kind是struct

    (3)通过反射介意让变量在interface{}和reflect.Value之间相互转换;

    (4)使用反射的方式来获取变量的值,要求数据类型匹配;

    (5)通过反射来修改变量,注意当使用SetXxx方法来设置需要通过对指针类型来完成,这样才能改变传入变量的值,同时需要使用到reflect.Value.Elem()方法;即rVal.Elem().SetInt(20)

    具体实践;

    使用反射来遍历结构体的字段,并调用结构体的方法,并获取结构体标签的值。

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type monster struct {
        Name  string `json:"name"`
        Age   int    `json:"monster_age"`
        Score float32
        Sex   string
    }
    
    func (m monster) Print() {
        fmt.Println("----strat----")
        fmt.Println()
        fmt.Println("----end----")
    }
    
    func (m monster) GetSum(n1, n2 int) int {
        return n1 + n2
    }
    
    func (m monster) Set(name string, age int, score float32, sex string) {
        m.Name = name
        m.Age = age
        m.Score = score
        m.Sex = sex
    }
    
    func testStruct(b interface{}) {
        //获得类型
        rtyp := reflect.TypeOf(b)
        //获得值
        rval := reflect.ValueOf(b)
        //获得类别
        kd := rval.Kind()
        //如果kd不是指针类型且rval.Elem()不是结构体类别
        if kd != reflect.Ptr && rval.Elem().Kind() == reflect.Struct {
            fmt.Println("expect struct")
            return
        }
        //获取结构体有几个字段
        num := rval.Elem().NumField()
        fmt.Printf("struct has %d fields
    ", num)
        //遍历每个字段
        for i := 0; i < num; i++ {
            //输出该字段的值
            fmt.Printf("field %d:值为=%v
    ", i, rval.Elem().Field(i))
            //获取该字段的json标签
            tagval := rtyp.Elem().Field(i).Tag.Get("json")
            //如果标签不为空,则打印出标签
            if tagval != "" {
                fmt.Printf("field %d:tag为=%v
    ", i, tagval)
            }
        }
        rval.Elem().Field(0).SetString("孙悟空")
        //获取结构体有几个方法
        numOfMethod := rval.Elem().NumMethod()
        fmt.Printf("struct has %d methods
    ", numOfMethod)
        //获取第二个方法,函数的顺序是按首字母的ACSII进行排序的,所以这里第二个方法是Print
        rval.Elem().Method(1).Call(nil)
        //定义一个reflect.Value切片
        var params []reflect.Value
        //添加两个整型
        params = append(params, reflect.ValueOf(10))
        params = append(params, reflect.ValueOf(40))
        //调用第一个方法,并传入参数(注意参数类型是reflect.Value切片)
        res := rval.Elem().Method(0).Call(params)
        //注意res是一个切片,里面只有一个值
        fmt.Println("res=", res[0].Int())
    
    }
    
    func main() {
        var a monster = monster{
            Name:  "tom",
            Age:   400,
            Score: 20.9,
        }
        testStruct(&a)
        fmt.Println(a)
    }

    要在反射中修改结构体的字段的值,必须传入的是地址,并且在反射中使用.Elem().SetXxx()

    具体实践2:通过反射完成适配器

    func main() {
        test1 := func(v1 int, v2 int) {
            fmt.Printf("v1=%d,v2=%d
    ", v1, v2)
        }
        test2 := func(v1 int, v2 int, s string) {
            fmt.Printf("v1=%d,v2=%d,s=%s", v1, v2, s)
        }
        bridge := func(call interface{}, args ...interface{}) {
            //获取参数长度
            n := len(args)
            //定义参数切片
            params := make([]reflect.Value, n)
            //将参数存入到切片中
            for i := 0; i < n; i++ {
                params[i] = reflect.ValueOf(args[i])
            }
            // for i := 0; i < n; i++ {
                // fmt.Println(args[i])
            // }
            //获取函数名
            function := reflect.ValueOf(call)
            //将参数传给函数,并调用函数
            function.Call(params)
        }
    
        bridge(test1, 1, 2)
        bridge(test2, 1, 2, "test2")
    
    }

    运行结果:

    具体实践3:通过反射操作任意结构体类型

    type user struct {
        UserID string
        Name   string
    }
    
    func testReflectStruct() {
        var (
            model *user
            sv    reflect.Value
        )
        model = &user{}
        sv = reflect.ValueOf(model)
        //这里是指针类别
        fmt.Println(sv.Kind().String())
        sv = sv.Elem()
        //这里是结构体类别
        fmt.Println(sv.Kind().String())
        sv.FieldByName("UserID").SetString("1")
        sv.FieldByName("Name").SetString("tom")
        fmt.Println(model)
    }

     

  • 相关阅读:
    python-day1
    go 字符串方法
    str,转换int,相互
    go 文件打包上传本地测试环境
    通联收银宝,官方文档
    go uuid
    go xid
    golang decimal处理插件包 大数字处理
    图像处理 bimg
    golang strings,常用函数
  • 原文地址:https://www.cnblogs.com/xiximayou/p/11963372.html
Copyright © 2011-2022 走看看