func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
以上函数都是空接口类型,可以传入任意值,之后我们将得到一个type和Value类型变量
这里的type和Value都是反射包中定义的结构体,我们可以通过该结构体调用包下方法
package main
import (
"fmt"
"reflect"
)
func main(){
fmt.Println("--------TypeOf--------")
sum := 100
test1 := reflect.TypeOf(sum) //调用TypeOf函数接收一个值,返回声明好的Type结构体
fmt.Println(test1.Name()) //通过调用结构体下的方法和字段获取数据
fmt.Println(test1.Kind())
fmt.Println("--------ValueOf--------")
test2 := reflect.ValueOf(sum) //和typeOf类似,不同的是返回的Value结构体
fmt.Println(test2.Type()) //Value主要是用于处理数据相关的
fmt.Println(test2.Int())
}
返回
--------TypeOf--------
int
int
--------ValueOf--------
int
100
变量、interface{} 和reflect.Value是可以相互转换的
我们一般会在程序中添加一个函数用于接收空接口的数据
package main
import (
"fmt"
"reflect"
)
type Stu struct {
Name string
}
func test(b interface{}) {
rVal := reflect.ValueOf(b) //将传入的变量转换为Value类型
iVal := rVal.Interface() //在实际使用中,我们可能需要将Value转换为普通类型
//reflect.Value 结构体下有Interface()方法转换回空接口模式
v := iVal.(Stu) //我们将空接口类型通过类型断言转换回原来的类型
fmt.Println(v)
}
func main(){
test(Stu{Name: "123456"}) //调用
}
如上,我们先将数据转换为Value结构体,然后再通过Interface方法转换为空接口
然后通过类型推导,将空接口类型转换回原始数据,以上就是类型转换的方法
类型转换流程图
reflect.Value是一个结构体类型,我们将数据传入进去后因为类型不同无法使用
该结构体提供了很多的方法,直接转换成对应的数据类型进行交互
比如常见的int、string、bool等等,需要什么类型我们就转换成什么类型
案例
package main
import (
"fmt"
"reflect"
)
func reflectTest01(b interface{}) {
rVal := reflect.ValueOf(b)
n1 := 2 + rVal.Int() //只要基本符合运算要求就可以转换
fmt.Println(n1)
}
func main(){
var num int = 100
reflectTest01(num)
}
Kind返回变量持有的值的分类,如果变量是Value零值,返回值为Invalid
kind 表示一个大的分类,比如你int8 int16 int32 int,到kind这里都是int
package main
import (
"fmt"
"reflect"
)
type Student struct{
Name string
Age int
}
func reflectTest02(b interface{}) {
//方法1 通过type类型的kind方法获取
rtype := reflect.TypeOf(b)
fmt.Println(rtype.Kind())
//方法2 通过Value结构体的kind方法获取
rVal := reflect.ValueOf(b)
fmt.Println(rVal.Kind())
}
func main(){
stu := Student{
Name : "tom",
Age : 20,
}
reflectTest02(stu)
}
type 是类型,kind是类别,type和Kind可能是相同的,也可能是不同的
package main
import (
"fmt"
"reflect"
)
type Test struct {
Name string
}
func reflectTest01(b interface{}) {
rVal := reflect.ValueOf(b)
fmt.Println(rVal.Type())
fmt.Println(rVal.Kind())
}
func main(){
var num int = 100
reflectTest01(num)
var test Test = Test{Name:"123"}
reflectTest01(test)
}
返回
int
int
main.Test
struct
如上,当传入的是int类型,kind和type是相同的
当传入的是结构体类型,type会找到是那个结构体被传入了,kind只会返回这是一个结构体
通过反射来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针来完成
package main
import (
"fmt"
"reflect"
)
func reflect01(b interface{}){
rVal := reflect.ValueOf(b)
fmt.Printf("%v",rVal.Kind())
rVal.SetInt(20) //Value中有大量的Set方法,可以修改不同的数据类型
}
func main(){
var num int = 10
reflect01(&num) //当要在其他函数中修改传递的值需要传入内存地址
fmt.Println(num)
}
返回
ptr
panic: reflect: reflect.Value.SetInt using unaddressable value
但是我们不能直接*rVal这样无法转换,Value结构体下提供了一个Elem方法提供指针调用
package main
import (
"fmt"
"reflect"
)
func reflect01(b interface{}){
rVal := reflect.ValueOf(b)
fmt.Printf("%v\n",rVal.Kind())
rVal.Elem().SetInt(20) //这里的Elem简单可以理解为 *ptr 的意思
//取到指针具体指向内存空间的值,然后在做SetInt 修改变量的操作
}
func main(){
var num int = 10
reflect01(&num)
fmt.Println(num)
}
给你一个变量 var v float = 1.2
请使用反射得到他的reflect.Value,然后获取对应的type、kind和值
并将reflect.Value转换成interface{} 再将interface{}转换成float
代码
package main
import (
"fmt"
"reflect"
)
func reflect01(b interface{}){
rtype := reflect.TypeOf(b)
rVal := reflect.ValueOf(b)
fmt.Println(rtype,rVal,rVal.Kind())
iV := rVal.Interface()
fmt.Println(iV.(float))
}
func main(){
var v float = 1.2
reflect01(v)
fmt.Println(v)
}
package main
import (
"fmt"
"reflect"
)
func main(){
var str string = "tom" //ok
fs := reflect.ValueOf(str) //ok
fs.SetString("jack") //error 要求用指针去调用
fmt.Printf("%v\n",str)
}
返回
panic: reflect: reflect.Value.SetString using unaddressable value
package main
import (
"fmt"
"reflect"
)
func main(){
var str string = "tom"
fs := reflect.ValueOf(&str) //添加改为内存地址传入
fs.Elem().SetString("jack") //添加Elem为指定指针的值
fmt.Printf("%v\n",str)
}
使用反射来遍历"结构体字段",调用"结构体的方法",并获取"结构体标签"的值
func (v Value) Method(i int) Value
//获取结构体下所有的方法名
//方法名以顺序对应i值, i默认等于0
//我们可以指定返回的方法是索引的第几个
func (v Value) Call(in []Value) []Value
//当我们找到上面的方法后
//通过Call去调用该方法
package main
import (
"fmt"
)
type Monster struct{
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("---start---")
fmt.Println(s)
fmt.Println("---end---")
}
func (s Monster) GetSum(n1,n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string,age int, score float32,sex string){
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func main(){
a := Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
Sex: "xxx",
}
fmt.Println(a)
}
这里添加反射获取 数据类型、类别 并判断值是否为结构体类别
package main
import (
"fmt"
"reflect"
)
type Monster struct{
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("---start---")
fmt.Println(s)
fmt.Println("---end---")
}
func (s Monster) GetSum(n1,n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string,age int, score float32,sex string){
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}){
val := reflect.ValueOf(a)
Typ := val.Type()
kd := val.Kind() //通过Value获取到kind 类别
if kd != reflect.Struct{
fmt.Println("expect struct")
return
}
fmt.Println("传入的值是结构体类型","结构体类型是"+ Typ.String())
}
func main(){
a := Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
Sex: "xxx",
}
TestStruct(a)
}
package main
import (
"fmt"
"reflect"
)
type Monster struct{
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("---start---")
fmt.Println(s)
fmt.Println("---end---")
}
func (s Monster) GetSum(n1,n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string,age int, score float32,sex string){
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}){
val := reflect.ValueOf(a)
num := val.NumField() //NumField 方法上面说过,返回这个结构体有几个字段,以索引的形式
fmt.Printf("struct has %d fields\n",num) //我们上面定义了4个字段,返回应该是4
for i := 0 ;i < num;i++ {
fmt.Printf("Field %d: 值为=%v\n", i, val.Field(i)) //Field方法 用于查询对应索引位的字段名是什么
}
}
func main(){
a := Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
Sex: "xxx",
}
TestStruct(a)
}
package main
import (
"fmt"
"reflect"
)
type Monster struct{
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("---start---")
fmt.Println(s)
fmt.Println("---end---")
}
func (s Monster) GetSum(n1,n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string,age int, score float32,sex string){
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}){
typ := reflect.TypeOf(a) //这里注释恢复一下 因为标签属于类型中的
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Struct{
fmt.Println("expect struct")
return
}
num := val.NumField()
fmt.Printf("struct has %d fields\n",num)
for i := 0 ;i < num;i++ {
fmt.Printf("Field %d: 值为=%v\n", i, val.Field(i))
tagVal := typ.Field(i).Tag.Get("json") //查询索引位的字段,Tag标签是否存在值
//请求key为json的tag 别名
if tagVal != "" { //如果该字段有标签,就显示,否则就不显示
fmt.Printf("Field %d: tag为=%v\n",i,tagVal)
}
}
}
func main(){
a := Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
Sex: "xxx",
}
TestStruct(a)
}
返回
struct has 4 fields
Field 0: 值为=黄鼠狼精
Field 1: 值为=400
Field 2: 值为=30.8
Field 3: 值为=xxx
Field 0: tag为=name
Field 1: tag为=monster_age //其他没有配置json,所以没有别名
上面有一段Field(i).Tag.Get("json") 没说这个是那里蹦出来的
下面找一下,登陆官方文档找到reflect包
https://studygolang.com/pkgdoc
我们上面那个是走的typeOf的,返回的是type结构体,这里我们点type Type
在Type这个结构体下寻找 Field方法查询字段信息
可以看到这个Field方法 返回的是一个 叫StructField的类型,我们来继续查一下
可以看到返回的类型是一个结构体,这个结构体下有包含一些结构体的字段信息
我们上面要查询的是结构体的标签,所以选择了Tag字段,而该字段的类型是StructTag
这个字段是一个string类型,下面提供了如何去请求这个字段的方法
需要给Get传入我们要匹配的key是名称,我们上面都是json开头是
package main
import (
"fmt"
"reflect"
)
type Monster struct{
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("---start---")
fmt.Println(s)
fmt.Println("---end---")
}
func (s Monster) GetSum(n1,n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string,age int, score float32,sex string){
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}){
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Struct{
fmt.Println("expect struct")
return
}
num := val.NumField()
for i := 0 ;i < num;i++ {
tagVal := typ.Field(i).Tag.Get("json")
if tagVal != "" {
}
}
numOfMethod := val.NumMethod() //返回一共有多少个方法
fmt.Printf("struct has %d methods\n",numOfMethod)
val.Method(1).Call(nil) //Method调用第二个方法,这里是走索引0-2
//Call 调用当前拿到的方法
}
func main(){
a := Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
Sex: "xxx",
}
TestStruct(a)
}
返回
struct has 3 methods
---start---
{黄鼠狼精 400 30.8 xxx}
---end---
这里又有疑问了,我怎么知道那个索引对应的是那个方法?
上面不是用的索引位是第二位吗,为什么调用到了第一个的方法? 下面说明
Method方法是依据你定义方法的首字母ASCII码进行排序的
以上3个方法 GetSum、Print、Set,通过首字母的ASCII码排序后实际上的顺序
GetSum(0)、Print(1)、Set(2)因为Print是不需要传参的,所以给一个nil
package main
import (
"fmt"
"reflect"
)
type Monster struct{
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("---start---")
fmt.Println(s)
fmt.Println("---end---")
}
func (s Monster) GetSum(n1,n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string,age int, score float32,sex string){
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}){
val := reflect.ValueOf(a)
var params []reflect.Value //定义一个type Value 类型的切片
params = append(params,reflect.ValueOf(10)) //将要传递的值做常量加入到切片中
params = append(params,reflect.ValueOf(40))
res := val.Method(0).Call(params) //将整个切片利用Call提交出去,返回的值也是[]reflect.Value
fmt.Println("res=",res[0].Int()) //因为此时res中的类型是[]reflect.Value
//他里面只有一个值,我们使用0索引为调用,并转换为int食醋胡
}
func main(){
a := Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
Sex: "xxx",
}
TestStruct(a)
}
返回
res= 50
使用反射的方式来"获取结构体的tag标签","遍历"字段的值
package main
import (
"fmt"
"reflect"
)
type Monster struct{
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("---start---")
fmt.Println(s)
fmt.Println("---end---")
}
func (s Monster) GetSum(n1,n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string,age int, score float32,sex string){
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}){
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
//下面我们传入了内存地址,这里对比是否是结构体要修改
//要多一层判断,判断是否是指针,并且还要用elem去获取指针的信息(*ptr)
//根据获取的数据再去通过kind去获取传入的类型是什么,进而去对比
if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct{
fmt.Println("expect struct")
return
}
num := val.Elem().NumField() //因为是指针,我们这里获取字段的长度前面也要加个elem表示这个值是一个指针
val.Elem().Field(0).SetString("liuwei") //我们通过setString去修改 索引位0的值,同样要加Elem
for i := 0 ;i < num;i++ {
fmt.Printf("Field %d: 值为=%v\n", i, val.Elem().Field(i))
tagVal := typ.Elem().Field(i).Tag.Get("json") //只要是获取这个指针的值信息的,都要带elem
if tagVal != "" {
//fmt.Printf("Field %d: tag为=%v\n",i,tagVal)
}
}
tag := typ.Elem().Field(0).Tag.Get("json")
fmt.Printf("tag=%s\n",tag)
numOfMethod := val.Elem().NumMethod()
fmt.Printf("struct has %d methods",numOfMethod)
val.Elem().Method(1).Call(nil)
}
func main(){
a := Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
Sex: "xxx",
}
TestStruct(&a) //传入内存地址
fmt.Println(a)
}
返回
Field 0: 值为=liuwei
Field 1: 值为=400
Field 2: 值为=30.8
Field 3: 值为=xxx
tag=name
struct has 3 methods---start---
{liuwei 400 30.8 xxx}
---end---
{liuwei 400 30.8 xxx}
定义两个函数test1、test2 定义一个适配器函数,用做统一处理接口
package main
import (
"fmt"
"reflect"
)
func Call1(v1 int,v2 int){ //定义两个功能函数
fmt.Println(v1 / v2)
}
func Call2(v1 int,v2 int,str string){
fmt.Println(v1 + v2 ,str)
}
//定义一个通用的函数适配器
//通过args ...interface{} 用于接收多个参数
func bridge(call interface{},args ...interface{}) {
n := len(args) //先统计传入的参数长度
inValue := make([]reflect.Value,n) //根据传入的参数数量 创建一个type Value的切片
for i := 0; i < n;i++{ //因为传入的是多个参数,接收到的是切片
inValue[i] = reflect.ValueOf(args[i]) //循环将接收到的参数 以常量的形式加入到[]reflect.Value切片中
}
function := reflect.ValueOf(call) //将传入的函数转换为Value结构体类型
function.Call(inValue) //使用Value Call方法直接执行传入的函数,并将[]Value的inValue 作为参数交给函数使用
}
func main() {
bridge(Call1,1,2) //封装好后,直接输如函数加参数即可
bridge(Call2,1,2,"test2")
}
使用反射操作任意结构体类型
package main
import (
"fmt"
"reflect"
)
type user struct{
Userld string
Name string
}
func main(){
model := &user{}
sv := reflect.ValueOf(model)
fmt.Println("reflect.ValueOf",sv.Kind().String())
sv = sv.Elem() //可以在这里做一下 指针指定,下面就不用指定了
fmt.Println("reflect.ValueOf.Elem",sv.Kind().String())
//FieldByName指定要修改的字段
//SetString 修改字段信息
sv.FieldByName("Userld").SetString("12345678")
sv.FieldByName("Name").SetString("nickname")
fmt.Println("model",model)
}
package main
import (
"fmt"
"reflect"
)
type user struct{
Userld string
Name string
}
func main(){
var (
model *user
st reflect.Type
elem reflect.Value
)
st = reflect.TypeOf(model)
fmt.Println("reflect.TypeOf",st.Kind().String()) //ptr
st = st.Elem() //这里指定一下指针,下面就不用指定elem
fmt.Println("reflect.TypeOf.Elem",st.Kind().String()) //struct
//定义一个结构体
elem = reflect.New(st) //这里的New返回一个Value类型值,该值持有一个指向类型为st的新申请的零值的指针,
// 返回值的Type为PtrTo(st)。
fmt.Println("reflect.New",elem.Kind().String()) //ptr
fmt.Println("reflect.New.Elem",elem.Elem().Kind().String()) //Struct
//可以看到我们New出来的Value类型和上面提前定义的结构体类似
model = elem.Interface().(*user) //这里通过转换位空接口后 类型推导为*user的结构体类型
//并将我们声明出来的这个结构体又赋予给了开头的model结构体变量
//但是我们现在这个elem是一个内存地址啊,赋予到了model那么elem就和model数据同步了
elem = elem.Elem() //这里通过Elem这里将内存地址进行调用,类似*ptr
//model现在拿的是elem给的值,我们还可以通过elem去动态修改其他值
//当model或elem被修改时,对方也会同步数据
elem.FieldByName("Userld").SetString("12345678") //通过修改elem会影响到model变量
elem.FieldByName("Name").SetString("nickname")
fmt.Println("model model.name",model,model.Name)
}
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- oldu.cn 版权所有 浙ICP备2024123271号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务