反射使用的地方

  1. 序列化和反序列化时,如果希望序列时将结构体字段名称大写转换成小写,json:"xxx" 这里就用到了反射。
  2. 两个匿名函数变量,定义一个适配器函数用作统一处理接口:
    适配器函数:假设匿名函数名字为<匿名函数名称>,匿名函数中参数为a,b… 则适配器函数为 func(<匿名函数名称>,a,b…)
    就是说建立一个模板,匿名函数函数名称和匿名函数中的参数都作为适配器函数的参数传递。
  3. 反射价值在于自己可以开发go框架。

反射原理

  1. 反射可以在运行时动态获取变量的各种信息,比如变量的类型type,类别kind。
    类型和类别如果都是普通数据类型,没有区别;
    如果是结构体变量,类型和类别是有区别的;

  2. 通过反射,可以修改变量的值,可以调用关联的方法。

  3. 使用反射需要 import "reflect"

反射应用

  1. 常用的两个函数: reflect.TypeOf(<变量>) ; reflect.ValueOf(<变量>)

  2. 使用案例说明,案例后面均有注释说明。

    演示1: 基本数据类型 ≒ interface{} ≒ reflect.Value

    package main
    import ("fmt""reflect"
    )
    func main() {var num int = 100/*问题一:通过反射拿到num的rtype和rvalue注意区分反射拿到的值前面加个r做区分,reflect.rtype和type不是同一个类型这些*reflect.rtype 或 reflect.Value 是有方法的,和普通数据类型是不一样的。*/rty := reflect.TypeOf(num)           // int 注意这个int不是普通数据类型int,是有很多方法的rval := reflect.ValueOf(num)         // 100 注意这个100不是普通值为100,是有很多方法的fmt.Printf("%T %v\n",rty,rty)   //*reflect.rtypefmt.Printf("%T %v\n",rval,rval)  // reflect.Value/*问题二:举例说明,rval如果是int类型,那么可以加减乘除,下面验证*/n1 := 10n2 := 2 + n1fmt.Println(n2)//fmt.Println(n2 + rval)  //invalid operation: n2 + rval (mismatched types int and reflect.Value)/*如何解决上述问题func (v Value) IsNil() boolfunc (v Value) Kind() Kindfunc (v Value) Type() Typefunc (v Value) Convert(t Type) Valuefunc (v Value) Elem() Valuefunc (v Value) Bool() boolfunc (v Value) Int() int64*/fmt.Println(int64(n2) + rval.Int())  //提供将reflect.Value转化为int64的方法,这样强转一下就好了/*问题三:如何将reflect.Value转换为最初的int:只需要将reflect.Value转为空接口,再把空接口断言成需要转换的数据类型即可*/num_ori := rval.Interface().(int)fmt.Printf("%T %v\n",num_ori,num_ori)  //int 100
    }
    

    演示2: 结构体类型 ≒ interface{} ≒ reflect.Value

    package main
    import ("fmt""reflect"
    )
    type Kunkun struct {Name stringAge intSkill string
    }
    func main() {/*定义一个结构体实例caixukun,通过反射拿到type和value*/var caixukun = Kunkun{"蔡徐坤",20,"Ctrl"}cxkrty := reflect.TypeOf(caixukun)cxkrval := reflect.ValueOf(caixukun)fmt.Printf("%T %v\n",cxkrty,cxkrty)   // *reflect.rtype  main.Kunkunfmt.Printf("%T %v\n",cxkrval,cxkrval)  // reflect.Value {蔡徐坤 20 Ctrl} 仅在运行时知道类型,编译阶段过不去//将拿到的值转换成空接口,如果这是想取cxkival中的字段值是无法取出来的cxkival := cxkrval.Interface()fmt.Printf("%T %v\n",cxkival,cxkival) //main.Kunkun {蔡徐坤 20 Ctrl}fmt.Println(caixukun.Name)//fmt.Println(cxkival.Name)  //cxkival.Name undefined (type interface {} is interface with no methods)//将空接口类型断言成结构体类型就可以使用字段了cxk_ori, ok := cxkival.(Kunkun)  //已经将空接口类型数据断言成Kunkun结构体if ok {fmt.Println("成功断言取出字段名称: ",cxk_ori.Name)    //蔡徐坤}
    }
    

反射注意事项和细节

  1. reflect.Value.Kind(),reflect.rType.Kind() 获取的是变量的类别,返回的是一个常量,区别于类型而言,类别是类型的集合。

    var num int = 10    // type: int , Kind: int  这是普通数据类型
    var cxk Kunkun      // type: Kunkun , Kind: Struct 结构体就有宏观和微观概念了,例如Kunkun是自定义type,是结构体的一类,Struct包含Kunkun这种结构体。
    
  2. 使用反射的方式获取变量的值,要求数据类型匹配,否则会panic

  3. 通过反射来修改变量的值,见下面两个演示:

    演示1:通过反射改变普通变量的值

    package mainimport ("fmt""reflect"
    )
    func main() {/*1. 通过反射修改普通变量的值2. 通过反射修改结构体实例的值*///var cxk = Kunkun{"蔡徐坤",20,"Ctrl"}var num int = 100//使用反射修改普通变量的值,如果要改变变量值,必须传递地址,通过改变内存地址中的值完成变量改变rNum := reflect.ValueOf(num)fmt.Printf("%T %v\n",rNum,rNum)//rNum.SetInt(20)                 //reflect.Value.SetInt using unaddressable value,因为不是地址格式的数据所以不行//rNum.Elem().SetInt(20)          //把num变成20fmt.Printf("%T %v\n",num,num)   //这样改变肯定无效,因为不是指针格式//下面为正确做法rNum1 := reflect.ValueOf(&num)   //必须是地址,不然没有意义,无法改变num的值fmt.Printf("%T %v\n",rNum1,rNum1)rNum1.Elem().SetInt(20)          //把num变成20,rNum1为地址,rNum1.Elem()为一个value,Elem()用的最多,一定要注意fmt.Printf("%T %v\n",num,num)    //如果没有Elem(),unaddressable value panic
    }
    

    演示2:通过反射改变结构体字段的值

    package mainimport ("fmt""reflect"
    )type kunkun struct {Name stringAge intSkill string
    }func main() {/*上面理论讲的很清楚了,下方与上方修改普通变量几乎没有区别,唯一在于传入的地址变成了字段 &(cxk.Skill)*/var cxk = kunkun{"蔡徐坤",20,"Ctrl"}rCxk := reflect.ValueOf(&(cxk.Skill))rCxk.Elem().SetString("唱跳rap篮球")      //相当于改变了原来结构体实例中的技能变为 "唱跳rap篮球"fmt.Println(cxk.Skill)
    }
    

★★★反射的最佳实践

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

package mainimport ("fmt""reflect"
)//kunkun结构体
type caixukun struct {Name string   `json:"name"`Age int          `json:"age"`Skill string
}
func (c caixukun) Intro() {      // reflect.Value.Method()中 i=0fmt.Println("大家好,我是练习时长两年半的练习生cxk!")
}
func (c caixukun) Dance() {      // reflect.Value.Method()中 i=1fmt.Println("鸡你太美~~丁丁丁丁丁丁丁~鸡你太美~~~丁丁丁丁丁丁丁丁~~")
}
func (c caixukun) Set(n1 int,n2 int) int {      // // reflect.Value.Method()中 i=2return n1 + n2
}func Ref(cxk interface{}) {rty := reflect.TypeOf(cxk)    //获取结构体变量的反射类型rval := reflect.ValueOf(cxk)  //获取结构体变量的反射值rkd := rval.Kind()            //获取结构体变量的类型常量//fmt.Printf("%T %v\n",rty,rty)   //*reflect.rtype main.caixukun//fmt.Printf("%T %v\n",rval,rval) //reflect.Value {蔡徐坤 20 Ctrl}//fmt.Printf("%T %v\n",rkd,rkd)   //reflect.Kind structif rkd != reflect.Struct {          //Struct为常量比较fmt.Println("unexpect struct, re-input the correct struct, exit Program~")return}//获取结构体实例所有字段和值Fnum := rval.NumField()    //常用的一个方法,获得结构体方法数量,类似len()和 cap() 3个字段//接下来演示如何拿到结构体实例的字段和值for i := 0 ; i < Fnum ; i++ {/*Name string   `json:"name"`   //有tagAge int          `json:"age"`    //有tagSkill string                  //无tag*/fmt.Printf("字段索引值为%d,字段值为%v\n",i,rval.Field(i)) //reflect.Value.Field(x)获得字段值//检查结构体实例字段的tag标签tagRval := rty.Field(i).Tag.Get("json")fmt.Printf("第%d个字段为%v,对应的tag为%v\n",i,rval.Field(i),tagRval)}//获取结构体实例有多少个方法以及方法调用methods := rval.NumMethod()fmt.Printf("结构体实例存在%d个方法\n",methods)//reflect.Value.Method(x int)方法中,方法索引排序并不是按照结构体代码由上到下,而是按照方法首字母ASCII代码码值进行//所以方法0为:Dance(),方法1为:Intro(),方法2为:Set(xxx),如果方法中本身不传参,Call中必须填入(nil)rval.Method(1).Call(nil)   //调用Intro方法: 大家好,我是练习时长两年半的练习生cxk!rval.Method(0).Call(nil)   //调用Dance方法: 鸡你太美~~...//对于要传递参数的方法,传进call()中的是一个[]reflect.Value切片,所以需要先定义切片var slice []reflect.Valueslice = append(slice,reflect.ValueOf(30))   //传第一个参数 进切片slice = append(slice,reflect.ValueOf(20))   //传第二个参数 进切片fmt.Println(rval.Method(2).Call(slice)[0])  //返回值只有一个值,也是一个切片,所以只需要接收切片第一个值[0]}func main() {/*使用反射来遍历结构体字段,调用结构体的方法,并获得结构体的标签值两个重要方法1. reflect.Value.Method() :func (v Value) Method(i int) Value默认按照传入结构体实例的方法名排序对应的i值,初始值i=0见上方结构体方法注释,三个方法,分别为方法0,1,2,返回值是一个reflect.Value2. reflect.Value.Call() :func (v Value) Call(in []Value) []Value通过Method获取的几个Value,作为参数形式传入Call()中,调用几种方法,注意[]value为reflect.Value的切片*/var cxk = caixukun{"蔡徐坤",20,"Ctrl"}Ref(cxk)
}

Go核心开发学习笔记(廿九) —— 反射相关推荐

  1. Go核心开发学习笔记(九)—— 顺序控制,分支控制

    程序流程控制 决定程序如何执行,常用三大流程控制语句 顺序控制 分支控制:if-else 循环控制:for 符合条件前循环控制,符合条件后循环控制(笔记十去记录) 顺序控制 从上到下依次执行,每个程序 ...

  2. Polyworks脚本开发学习笔记(十九)-将数据对象与参考对象对齐的方法

    Polyworks脚本开发学习笔记(十九)-将数据对象与参考对象对齐的方法 把开发手册理了一遍,发现还有几个点没有记录下来,其中一个就是使用点对的粗对齐和使用参考目标的精确对齐.为了把这个学习笔记凑够 ...

  3. Windows 8 Directx 开发学习笔记(九)材质定义及混合光照效果实现

    在真实环境中,同一个物体在不同光源照射下的颜色并不一样,因为物体本身并没有颜色,而是它会反射不同颜色的光.物体对不同颜色光的吸收率.反射率,加上光泽度.透明度等其他物理属性组合在一起,定义了这个物体的 ...

  4. 逆向工程核心原理学习笔记(九):小端序标记法2

    程序地址:http://t.cn/RXnT2pD 我们用OD查看小端序. 代码如下: 我们编译,然后拖进OD查看,直接跳到0x401000入口点. 我们看到几个位置: 由此处我们可以推测后面括号中存放 ...

  5. Polyworks脚本开发学习笔记(九)-公差控制及制作报告

    Polyworks脚本开发学习笔记(九)-公差控制及制作报告 定义公差 系统有默认的公差设置,可以在选项中进行系统的默认设置,但往往不是想要的.比如下图的XYZ三向都是-/+1,我想只控制Y向并且公差 ...

  6. Kinect开发学习笔记之(六)带游戏者ID的深度数据的提取

    Kinect开发学习笔记之(六)带游戏者ID的深度数据的提取 zouxy09@qq.com http://blog.csdn.net/zouxy09 我的Kinect开发平台是: Win7x86 + ...

  7. Android 开发学习笔记:七大知识点板块汇总

    前言 我从事 Android 开发行业也有些年头,工作期间也接触过很多 Android 开发者, 因此也非常清楚 程序员最大的限制并非年龄而是实力: 但大多数初中级Android工程师,想要提升技能, ...

  8. Go语言开发学习笔记(持续更新中)

    Go语言开发学习笔记(持续更新中) 仅供自我学习 更好的文档请选择下方 https://studygolang.com/pkgdoc https://www.topgoer.com/go%E5%9F% ...

  9. FFmpeg基础到工程-多路H265监控录放开发学习笔记

    多路H265监控录放开发学习笔记 课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等.包括:音视频.流媒体.直播.Android.视频监控28181.等. 具体 ...

最新文章

  1. Codeforces Round #640 (Div. 4)(ABCDEG题解)
  2. hdu 4722(记忆化搜索)
  3. Redux源码浅析系列(一):`CreateStore`
  4. SpringBoot高级消息-RabbitMQ运行机制
  5. 【图论】【最短路】【Dijkstra】最小花费(ssl 2206/luogu 1576)
  6. mmd python error_python_mmdt:一种基于敏感哈希生成特征向量的python库(一)
  7. 容器,VM和Docker的初学者友好介绍
  8. [转]调整 VirtualBox 虚拟机的磁盘大小
  9. Arcgis Javascript那些事儿(八)--图层获取与图层顺序
  10. 获取本机内网、外网ip
  11. python爬虫基本概述
  12. 维谛(Vertiv)培训中心迁新址,再攀培训业务新高峰
  13. 怎么恢复我在计算机里删掉的文档,如题,如何彻底删除电脑中的文件,使文件不能恢复?(我的方式是直接? 爱问知识人...
  14. java显示数据库_java查询数据库中的数据并显示
  15. 嵌入式软件异步编程:请求的多阶段异步处理
  16. 最完整的dos命令字典,IIS服务命令,FTP命令
  17. html播放韰 寸 频,js根据文字获取首字母案例,直接复制在html中即可查看效果
  18. 了解RO、RW、ZI和.text .bss .data
  19. SpringBoot2-6 Web1-静态资源 默认4个目录,特别是resources 目录, webjars引入和内部资源访问,注意其内部resources目录 HandlerMapping
  20. mysql数据库,使用substring函数截取字符串返回空问题

热门文章

  1. 十二、聚类算法——相似度测量
  2. 【如何保持稳定的情绪】
  3. 从Hyperledger创始人到打造平安75亿美元金融科技帝国,“务实”思考者陆一帆 | 人物志...
  4. 新加坡EMVCo支付二维码解析
  5. KEIL5 中 .PACK文件的制作
  6. 【细品C++】命名空间详解(namespace)
  7. 美妆品牌巴黎欧莱雅官宣Kendall Jenner成为品牌代言人
  8. c语言读写文件 eof,C语言文件操作之EOF解析
  9. [ 英语 ] 如何解决那些让人恼火的介词?
  10. 计算机截取最高分和最低分的函数,Excel中竟真的有一函数,可以去掉最高分、最低分,计算最终得分...