Golang中的反射规则是什么


这篇文章主要介绍“Golang中的反射规则是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Golang中的反射规则是什么”文章能帮助大家解决问题。简单来看,反射就是在程序运行时期对程序本身进行访问和修改的能力,例如在程序运行时,可以修改程序的字段名称,字段值,还可以给程序提供接口访问的信息等等这是 Go 语言中提供的一种机制,我们可以在 Go 语言公共库中可以看到很多关于 reflect 的使用位置例如常用的 fmt 包,常用的 json 序列化和反序列化,自然前面我们说到的 gorm 库自然也是使用了反射的可是我们一般为什么要使用反射呢?根据反射的能力,自然是因为我们提供的接口并不知道传入的数据类型会是什么样的, 只有当程序运行的时候才知道具体的数据类型但是我们编码的时候又期望去校验程序运行时传入的类型会是什么样的(例如 json 的序列化)并对其这种具体的数据进行操作,这个时候,咱们就需要用到反射的能力了所以对于使用到反射的地方,你都能看到 interface{} 是不是就不奇怪了呢?正是因为不确定传入的数据类型会是什么样的,所以才设计成 interface{}首先关注反射的三个重要的定律,知道规则之后,我们按照规则玩就不会有什么问题,只有当我们不清楚规则,总是触发条款的时候,才会出现奇奇怪怪的问题反射是可以将 接口类型的变量 转换成 反射类型的对象反射可以将 反射类型的对象 转换成 接口类型的变量我们在运行时要去修改的 反射类型的对象 ,那么要求这个对象对应的值是要可写的对于上述 3 个规则也是比较好理解,还记的之前我们说过的 unsafe 包里面的指针吗?都是将我们常用的数据类型,转换成包(例如 unsafe包,或者 reflect 包)里面的指定数据类型,然后再按照包里面的规则进行修改数据相当于,换个马甲,就可以进行不同的操作了一般咱们先会基本的应用,再去研究他的原理,研究他为什么可以这样用,慢慢的才能理解的更加深刻实际上此处说的 接口类型的变量 我们可以传入任意数据类型的变量,例如 int, float, string ,map, slice, struct 等等反射类型的对象 这里就可以理解成 reflect 反射包中的 reflect.Type reflect.Value 对象,可以通过 reflect 包中提供的 TypeOfValueOf 函数得到其中 reflect.Type 实际上是一个 interface ,他里面包含了各种接口需要进行实现,它里面提供了关于类型相关的信息其中如下图可以查看到 reflect.Type 的所有方法,其中绿色的 是所有数据类型都是可以调用的红色的是 函数类型数据可以调用的黑色的是 Map,数组 Array,通道 Chan,指针 Ptr 或者 切片Slice 可以调用的蓝色的是结构体调用的黄色的是通道 channel 类型调用的reflect.Value 实际上是一个 struct,根据这个 struct 还关联了一组方法,这里面存放了数据类型和具体的数据,通过查看其数据结构就可以看出

typeValuestruct{
typ*rtype
ptrunsafe.Pointer
flag
}

看到此处的 unsafe.Pointer 是不是很熟悉,底层自然就可以将 unsafe.Pointer 转换成 uintptr,然后再修改其数据后,再转换回来,对于 Go 指针不太熟悉的可以查看这篇文章:GO 中的指针?写一个简单的 demo 就可以简单的获取到变量的数据类型和值

funcmain(){vardemoStrstring="nowreflect"
fmt.Println("type:",reflect.TypeOf(demoStr))
fmt.Println("value:",reflect.ValueOf(demoStr))
}

我们可以通过将 reflect.Value 类型转换成我们具体的数据类型,因为 reflect.Value 中有对应的 typ *rtype 以及 ptr unsafe.Pointer例如我们可以 通过 reflect.Value 对象的 interface() 方法来处理

funcmain(){vardemoStrstring="nowreflect"
fmt.Println("type:",reflect.TypeOf(demoStr))
fmt.Println("value:",reflect.ValueOf(demoStr))varresstring
res=reflect.ValueOf(demoStr).Interface().(string)
fmt.Println("res==",res)
}

首先我们看上书的 demo 代码,传入 TypeOfValueOf 的变量实际上也是一个拷贝,那么如果期望在反射类型的对象中修改其值,那么就需要拿到具体变量的地址然后再进行修改,前提是这个变量是可写的举个例子你就能明白

funcmain(){
vardemoStrstring="nowreflect"
v:=reflect.ValueOf(demoStr)
fmt.Println("iscanset",v.CanSet())
//v.SetString("helloworld")//会panic
}

可以先调用 reflect.Value 对象的 CanSet 查看是否可写,如果是可写的,我们再写,如果不可写就不要写了,否则会 panic那么传入变量的地址就可以修改了??传入地址的思路没有毛病,但是我们去设置值的方式有问题,因此也会出现上述的 panic 情况此处仔细看能够明白,反射的对象 v 自然是不可修改的,我们应该找到 reflect.Value 里面具体具体的数据指针,那么才是可以修改的,可以使用 reflect.Value Elem 方法看上了上述案例可能会觉得那么简单的案例,一演示就 ok,但是工作中一用就崩溃,那自然还是没有融会贯通,说明还没有消化好,再来一个工作中的例子一个结构体里面有 map,map 中的 key 是 string,value 是 []string需求是访问 结构体中 hobby 字段对应的 map key 为 sport 的切片的第1 个元素,并将其修改为 hellolworld

typeRDemostruct{
Namestring
Ageint
Moneyfloat32
Hobbymap[string][]string
}

funcmain(){
tmp:=&RDemo{
Name:"xiaomiong",
Age:18,
Money:25.6,
Hobby:map[string][]string{
"sport":{"basketball","football"},
"food":{"beef"},
},
}

v:=reflect.ValueOf(tmp).Elem()//拿到结构体对象
h:=v.FieldByName("Hobby")//拿到Hobby对象
h2:=h.MapKeys()[0]//拿到Hobby的第0个key
fmt.Println("key1name==",h2.Interface().(string))

sli:=h.MapIndex(h2)//拿到Hobby的第0个key对应的对象
str:=sli.Index(1)//拿到切片的第1个对象
fmt.Println(str.CanSet())

str.SetString("helloworld")
fmt.Println("tmp==",tmp)
}

可以看到上述案例运行之后有时可以运行成功,有时会出现 panic 的情况,相信细心的 xdm 就可以看出来,是因为 map 中的 key 是 无序的导致免费云主机域名,此处也提醒一波,使用 map 的时候要注意这一点看上述代码,是不是就能够明白咱们使用反射去找到对应的数据类型,然后按照数据类型进行处理数据的过程了呢有需要的话,可以慢慢的去熟练反射包中涉及的函数,重点是要了解其三个规则,对象转换方式,访问方式,以及数据修改方式那么通过上述案例,可以知道关于反射中数据类型和数据指针对应的值是相当重要的,不同的数据类型能够用哪些函数这个需要注意,否则用错直接就会 panic来看 TypeOf 的接口中涉及的数据结构在 reflect 包中 rtype 是非常重要的,Go 中所有的类型都会包含这个结构,所以咱们反射可以应用起来,结构如下

//rtypemustbekeptinsyncwith../runtime/type.go:/^type._type.
typertypestruct{
sizeuintptr
ptrdatauintptr
hashuint32
tflagtflag
alignuint8
fieldAlignuint8
kinduint8
equalfunc(unsafe.Pointer,unsafe.Pointer)bool
gcdata*byte
strnameOff
ptrToThistypeOff
}

其中可以看到此处的 rtype 的结构保持和 runtime/type.go 一致 ,都是关于数据类型的表示,以及对应的指针,关于这一块的说明和演示可以查看文末的 interface{} 处的内容从 ValueOf 的源码中,我们可以看到,重要的是 emptyInterface 结构

//emptyInterfaceistheheaderforaninterface{}value.typeemptyInterfacestruct{
typ*rtype
wordunsafe.Pointer
}复制代码

emptyInterface 结构中有 rtype 类型的指针, word 自然是对应的数据的地址了reflect.Value 对象中的方法也是非常的多,用起来和上述说到的 reflect.Type 接口中的功能类似关于源码中涉及到的方法,就不再过多的赘述了,更多的还是需要自己多多实践才能体会的更好殊不知,此处的 reflect.Value 也是可以转换成 reflect.Type ,可以查看源码中 reflectvalue.gofunc (v Value) Type() Type {其中 reflect.Valuereflect.Type ,和任意数据类型 可以相互这样来转换如下图:关于“Golang中的反射规则是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注百云主机行业资讯频道,小编每天都会为大家更新不同的知识点。

相关推荐: vue如何封装axios请求

这篇文章主要介绍“vue如何封装axios请求”,在日常操作中,相信很多人在vue如何封装axios请求问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue如何封装axios请求”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 02/27 19:19
下一篇 02/27 19:19

相关推荐