GO-反射简介

反射概述

  • 通常的程序逻辑是:编码时先写好剧本,运行时按照写好的剧本演
  • 何时创建一个什么实例,给哪个属性赋什么值,然后调用它的哪个方法都毫厘不差
  • 但能否在运行时动态地生成“剧本”呢?
  • 根据具体的业务需要见机行事,动态地生成一个不知道具体类型会是什么的实例,动态地访问一个无法提前预知的属性或方法
  • 答案是可以的,这便引出了今天的主角——反射

应用场景举例:导出商品列表到Excel

  • 需求是:不管用户在界面上看到什么商品列表,当它捅一下导出按钮,就将该商品的所有属性和值写出为文件;
  • 本例的难点是:我们无法预知用户会选择导出什么类型的商品数据、它有哪些属性,也就无法相应地去创建Excel数据表的列(读写Excel有相应的库,你也可以选择写出为文本文件、写出到数据库表中等等)
  • 因为商品的种类太多,如果用正射去做,那么有多少商品类型我们就要写多少个switch或if分支,然后在每一个分支里根据当前分支的具体商品类型去构造相应的Excel数据列,这显然是蹩脚并且很难维护和扩展的
  • 而通过反射来做就易如反掌了,管你来的是什么商品实例,立即动态解析得到其类别、所有属性名值,然后根据属性名去创建Excel的列,根据属性值去做数据的填充和写入

定义结构体

type Human struct {
    Name string "姓名"
    Age  int    "年龄"
}

func (h *Human) GetName() string {
    fmt.Println("GetName called",h.Name)
    return h.Name
}

func (h *Human) SetName(name string) {
    fmt.Println("SetName called:", name)
    h.Name = name
}

func (h *Human) Eat(food string, grams int) (power int) {
    fmt.Println("Eat called:", food, grams)
    return 5 * grams
}

反射功能一览

func main() {

    //创建对象(在实际开发中可以是任意未知类型)
    h := Human{"bill", 60}

    //获取任意对象的类型和值
    getObjTypeValue(h)

    //获得任意对象的所有属性
    getObjFields(h)

    //获取任意对象的所有方法
    getObjMethods(&h)

    //修改对象任意属性的值
    modifyFieldValue(&h)

    //动态调用对象的任意方法
    callMethods(&h)
    fmt.Println("after:h=", h)
}

获取任意对象的类型和值

func getObjTypeValue(obj interface{}) {
    oType := reflect.TypeOf(obj)
    oKind := oType.Kind()
    fmt.Println(oType, oKind)

    oValue := reflect.ValueOf(obj)
    fmt.Println(oValue)
}

获得任意对象的所有属性

func getObjFields(obj interface{}) {
    oType := reflect.TypeOf(obj)
    oValue := reflect.ValueOf(obj)

    fieldsCount := oType.NumField()
    for i := 0; i < fieldsCount; i++ {

        //从对象值中获取第i个属性的值,进而值的“正射”形式
        fValue := oValue.Field(i).Interface()
        structField := oType.Field(i)
        fmt.Println(structField.Name, structField.Type, structField.Tag, fValue)
    }
}

获取任意对象的所有方法

func getObjMethods(obj interface{}) {
    oType := reflect.TypeOf(obj)
    fmt.Println(oType.NumMethod())
    for i := 0; i < oType.NumMethod(); i++ {
        method := oType.Method(i)
        fmt.Println(method.Name, method.Type)
    }
}

修改对象任意属性的值

func modifyFieldValue(objPtr interface{}) {
    //得到【指针的Value】
    oPtrValue := reflect.ValueOf(objPtr)
    //得到【指针所指向的值(结构体)的Value】
    oValue := oPtrValue.Elem()
    fmt.Println(oValue)

    //遍历所有属性的值
    for i := 0; i < oValue.NumField(); i++ {

        //根据序号拿到【属性的Value】
        fValue := oValue.Field(i)
        //拿到属性值的原生类型
        fKind := fValue.Kind()
        //fmt.Println(fKind)

        //根据不同的原生类型设置为不同的值
        switch fKind {
        case reflect.String:
            fValue.SetString("张全蛋")
        case reflect.Int:
            fValue.SetInt(99)
        case reflect.Bool:
            fValue.SetBool(false)
        default:
            fmt.Println("设置为其他值...")
        }
    }

}

动态调用对象的任意方法

func callMethods(objPtr interface{}) {

    //要通过对象的oType拿取方法的参数列表(oType.In(i))
    oType := reflect.TypeOf(objPtr)
    //要通过对象的oValue拿取方法的具体实现(methodValue)
    oValue := reflect.ValueOf(objPtr)

    //根据方法的数量进行遍历
    for i := 0; i < oType.NumMethod(); i++ {

        //预定义要传值的参数切片[]Value
        args := make([]reflect.Value, 0)

        //从对象的oType拿到当前的方法的methodType
        methodType := oType.Method(i).Type

        //根据参数个数进行遍历
        //为每个参数乱怼一个同种类型反射值,丢入参数列表
        //内层循环走完时,当前方法的参数列表就形成了
        for j:=0;j<methodType.NumIn();j++{

            //从方法的methodType获取当前参数artType
            artType := methodType.In(j)
            //再获取参数类型artType的原生类型
            argKind := artType.Kind()

            //根据不同的参数原生类型乱怼相同类型的值
            switch argKind {

            case reflect.String:
                //获取字符串"一坨翔"的反射值Value,丢入参数列表
                args = append(args,reflect.ValueOf("一坨翔"))

            case reflect.Int:
                args = append(args,reflect.ValueOf(100))
            case reflect.Bool:
                args = append(args,reflect.ValueOf(false))
            }
        }

        //从对象值oValue中获取当前方法值methodValue
        methodValue := oValue.Method(i)

        //使用参数列表+方法值,反射调用当前方法
        methodValue.Call(args)
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值