反射的最佳实践(1) | 学习笔记

简介: 快速学习反射的最佳实践(1)

开发者学堂课程【Go语言核心编程 - 面向对象、文件、单元测试、反射、TCP编程反射的最佳实践(1)】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/626/detail/9773


反射的最佳实践(1)

一、使用反射来遍历结构体的字段

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

在这个地方其实就解释了怎样去获取到结构体的标签。在写结构体的时候曾经写过节省冒号是怎样拿到的,这个里面就可以解释这个问题了。尤其是在调用结构体的方法的时候需要介绍两个特别重要的方法。一个叫做 method,另一个叫做 call。

func(Value)Method  func(v Value) Method(i int) Value 它默认按方法名排序对应 i 值,i 是从0开始的。

func(Value)Call  func(v Value) Call(in []Value) []Value 它传入参数和返回参数是 []reflect.Value。

这两个方法都是 reflect.value 这种类型的方法。

(1) Method

method 可以通过 reflect.value 填上这个 i 就可以返回这一个反射的 V 所持有的结构体的第几个方法。方法的时候这个 i 是从0开始的。比如这个结构体有三个方法,那么第一个方法的下标就为0,第二个方法的下标就为1,第三个方法的下标就为2。

(2) Call

获取到方法以后可以调用这个 call来真正的调用对应的方法。在 call 后面可以传一个切片,这个切片的类型是 reflect.Value,也就是什么类型的都可以往里面放,但是必须是 reflect.value 这种类型的切片往里面放。因为获取的方法可能有参数,这个时候通过切片往里面扔。

2、应用 apply

看第一个应用为 apply01,在这里新建一个文件。先把主包打上,还需输入一些相关的包包。首先 fmt 这个包肯定是要有的,reflect 这个包也肯定是要有的。

3、方法:

(1) 这里定义了一个 monster 的结构体,左边依次为姓名、年龄、成绩、性别,右边是它的标签。紧接着给 monster 绑定了一个 print 方法或者关联了一个 print 方法,输出这个 monster 的值,这是一个方法,就是输出或显示 s 的具体的值。

(2) 第二个方法写了一个 getsum,让它可以返回两个数的和。这也是一个方法。

(3) 第三个方法写了一个 set,这个方法可以接收4个值去给 Monster 赋值。

代码如下:

package main

import(

“fmt”

“reflect”

//定义了一个 monster 结构体

type Monster struct {

Name string json:`〝name〞`

Age int `json:〝monster_age〞`

Score float32

Sex string

//方法,显示 s 的值

func(s monster) Print() {

fmt. PrintIn(〝---start---〞)

fmt.PrintIn(s)

fmt.PrintIn(---end---)

//方法,返回两个数的和

func(s monster) GetSum(n₁,n₂,int){

return n₁﹢n₂

//方法,接收4个值,给 Monster 赋值

func(s monster) Set(name string,age int,score float32,sex string){

s.Name=name

s.Age=age

s.score=score

s.sex=sex

4、组函数

(1)怎样取字段、值以及标签的名称

这个是非常熟悉的名字“黄鼠狼精”。创建了一个 monster 的实例,紧接将 Monster 的实例传递给 TestStruct 这个函数Kd 如果不等于reflect.struct,在这里所要做的事就是看它是否为一个结构体。如果不是结构体的话就不用进行了。因为案例是要测试一个结构体的。所以在这里做了一个判断。因为目标是对一个结构体进行遍历。所以在反射里面这个地方提示是可以对类别进行判断的。因为刚才打印出来 kd 就是类别。注意 const 是在 reflect 这个包里面定义的,所以在使用的时候就应该是在 reflect 这个包下面点什么。比如结构体就是 struct,它比较的时候其实就是一个常量的比较。它是数字其实是可以比较大小的。为了不被其他的代码影响,所以先把下面的代码进行注销。如果碰到比较多的代码,也可以按照这个思路。假设加了一个字段,比如加的字段为 address string,保存后就看这里是怎么赋值的。因为不给的话是一个默认值,这样一执行应该返回5。所以反射还是很强大的,它马上就检测出来有5个字段。已经把所有的字段拿到了,下一步就遍历这个字段。i 是代表第几个字段,value 就是 reflect value,它里面有个方法叫 field。在 field 里面填入了一个 i 就代表返回了这个字段。把一个反射好的 value 一个整数打印就一次出来了,所以这个地方是可以把指令显示出来的。但是一定要注意不能对 val field(1) 进行任何的运算。比如返回来的是一个正数,是不能加减的。也可以把它转成一个相应的类型,可以用收支 tap。获取到标签的值就不能用 value 了。这个 type 它也有一个 field,但是前面的 field 返回的是一个 value,而 type 里面的 field 返回的是什么类型是一个关键点。在 type 里面有一个方法为 field,但它返回的类型是 StructField,和刚才那个是不同的。这也是一个结构体,结构体里面有一个 tag 标签。这个 tag 要把它的名字取出来,要把它对应的值取出来,后面有一个类型。点开后的 key 才真正返回 string,这个 key 就是结构体标签中的 json终于解释了为什么结构体反射可以拿到这个值了。可以想象到虚拟化里面有一段类似的代码,只是这个地方是可以变化的,虚拟化里面则不可以变化。所以说在虚拟化的时候这个标签必须写 jason。如果自己定义其实是可以改的。拿到名字过后要判断是否有值,因为有些字段有标签,有些字段没有标签。这个 type 的 field 和 value 的 field 是不一样的。第一个字段它的值是黄鼠狼精,还对应一个 tag 为 name。第一个字段即是第二个字段。它的值是400,对应的标签是 nonster ago。第三个字段的值为30.8。Field2 和 Field3 没有标签。比如加一个标签叫做成绩。这个时候按照 json 取的话就能遍历一个新的标签出来。那如果 json 写成 hsp 的话就拿不到了。因为读的时候是按照 key 读的。这个时候就拿不到成绩了,因为名称不对。这个就演示了怎样去拿它的字段,它的值以及它的标签的名称。

(2)代码如下:

Func main(){

//创建了一个 monster 的实例

var a Monster=Monster {

Name:“黄鼠狼精”,

Age:400,

Score:30.8,

//将 Monster 传递给 TestStruct 函数

func TestStruct(a interface {}) {

//获取 reflect.Type 类型

typ:=reflect Typeof(a)

//获取 reflect.value 类型

val:=reflect Valueof(a)

//获取到 a 对应的类别

Kd:=val.Kind()

//如果传入的不是 struct,就退出

If kd!=reflect.struct {

fmt PrintIn(〞expect struct〝)

return

//获取到该结构体有几个字段

num:=val. NumField()

fmt.Printf(〝struct has d% fields\n〞,num)//4

//变量结构体的所有字段

For i:=0;i<num;i++{

fmt Printf(〝Field %d:值为=%v\n,i,val.Field〞)

//获取到 struct 标签,注意需要通过 reflect.Type 来获取 tag 标签的值

tagVal:=typ.Field(i).Tag.Get(〞jason〝)

//如果该字段有 tag 标签就显示,否则就不显示

(3)方法

这个 value 是可以拿到的,总共有三个方法。在做高级项目时很可能用到这些东西。Val.method(1)是代表获取到它的第二种方法。第二种方法在调它的时候一个参数也没有填。call 表示的是调用该方法。两者可以分开写,也可以合在一起写。这里面有一个方法原则是它在进行方法排序的时候是按照函数的名字进行排序的。那反射是否有一种方法是根据反射名单进行反射的呢,理论上是有的。这里调的是没有函数的。这里还有两种方法,一个是 getsum,另一个是 set。它是需要传参数的。打开手册发现 call 这个方法它首先是获取到 method。调的时候要传一个 value 的切片,最后返回来的又是 reflect.Value 类型的切片。反射其实是主要通过 value 来进行的。所以它传的类型和返回来的类型是一样的。它是用一个类型都囊括在内了。

//获取到该结构体有多少种方法

numofMethod:=val.numofMethod()

fmt Printf(〝struct has %d methods \n〞, numofMethod)

var params [] reflect Value

Val.method(1).call(nil)

//获取到第二种方法,调用它

//方法的排序默认是按照函数名的排序(ASCII 码)

Val.method(1).Call(nil)

//调用结构体的第一个方法 method

var params [] reflect. Value// 声明了 [] reflect. Value

params=append(params,reflect.Valueof(10))

params=append(params,reflect.Valueof(40))

因为它是切片,所以 append 是可以动的。在反射调动的过程中其实是 reflect.Value 为主力的,字符串也是一样的。0代表的是第一个,第一个是求两个数的和,把这两个数放进去,最后调用完后这个 res 又是一个切片。传入的是一个切片,返回的仍是一个切片。取的时候一定要知道返回的是几个结果。Res[0]其实就是第一个切片,它其实只返回了一个。这个切片的大小其实也只有一个。从第一天开始讲 go 语言,调一个结构体的方法是结构的实例,再调用它的方法。现在变成了只需创建一个实例,传给一个方法,用反射就可以帮助做所有的事。如果做的是底层的框架,那把东西放进去就可以。比如有些框架它要求控制器必须以什么打头,在反射底层就可以找对应的入口函数,然后再调用,这就是一种框架就形成了。控制器是什么,函数名必须以什么打头,可以看出很多框架是有规范的。

相关文章
|
5月前
|
SQL 缓存 Java
ReflectionUtils反射工具:精要介绍与实战应用指南
ReflectionUtils反射工具:精要介绍与实战应用指南
|
安全 Java 测试技术
带你深入学习“反射”技术
带你深入学习“反射”技术
141 0
|
运维 Devops C#
【C#编程最佳实践 十七】反射工厂最佳实践
【C#编程最佳实践 十七】反射工厂最佳实践
90 0
|
SQL 监控 Java
魔法反射--java反射进阶(实战篇)
相信很多人在初学反射的时候也都会有这个想法(我就不相信就只有我一个人这么蠢!!) 大多数人不了解反射的原因并不是不了解, 而是不知道它到底能用来干什么 今天就来为大家分享一下反射的用法
85 0
|
安全 Java 程序员
魔法反射--java反射初入门(基础篇)
反射被应用于许多方面, spring的注解, jdbc的连接都是基于反射来实现的, 可能在工作中我们很少能用到反射, 但是在面试的过程中面试官经常会问道, 可以不用反射, 但作为一个程序猿, 还是应该了解了解的
96 0
|
网络协议 测试技术 Go
反射的快速入门(1)|学习笔记
快速学习反射的快速入门(1)
反射的快速入门(1)|学习笔记
|
网络协议 编译器 测试技术
反射的快速入门(2)|学习笔记
快速学习反射的快速入门(2)
反射的快速入门(2)|学习笔记
|
JSON 网络协议 数据建模
反射的最佳实践(2) | 学习笔记
快速学习反射的最佳实践(2)
|
网络协议 测试技术 Go
反射的注意事项和细节(1) | 学习笔记
快速学习反射的注意事项和细节(1)