go那些事儿|go反射使用第二弹(ValueOf)

memo012 · · 542 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

> 该文章始发于公众号【迈莫coding】 > 地址:[https://mp.weixin.qq.com/s/lgZykTL8ls6aG0OMNSbZMw](https://mp.weixin.qq.com/s/lgZykTL8ls6aG0OMNSbZMw) # 目录 - 概念 - ValueOf使用格式 - 反射获取值对象(ValueOf) - 通过canSet方法修改值 - 判断及获取元素的相关方法 - 值修改相关方法 - 值修改条件:可寻址 - 结构体值的非导出字段不能通过反射来修改 - 通过ValueOf访问成员对象 - 闲聊 - 欢迎加入我的公众号【迈莫coding】 一起pk大厂 # 概念 在程序运行期间,如果想要获取某个接口所代表的值,那么可以使用 reflect.ValueOf 进行获取,使用方式和 reflect.TypeOf 函数使用方式类型,都是传入一个interface{}接口的参数。当我们将一个接口值传递给一个 reflect.ValueOf 调用时,此调用返回的是代表着此接口值的动态值的一个reflect.Value值。 我们必须通过间接的途径获得一个代表一个接口值的 reflect.Value 值。 # ValueOf使用格式 当调用 reflect.ValueOf 函数时,他会返回一个 reflect.Value 对象,如果我们想要获取原值,则可通过具体方法来进行获取,具体方法如下: | 方法名 | 说 明 | |--|--| | Interface() interface {} | 将值以 interface{} 类型返回,可以通过类型断言转换为指定类型 | | Int() int64| 将值以 int 类型返回,所有有符号整型均可以此方式返回| | Uint() uint64|将值以 uint 类型返回,所有无符号整型均可以此方式返回 | | Float() float64 |将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回 | |Bool() bool |将值以 bool 类型返回 | |Bytes() []bytes |将值以字节数组 []bytes 类型返回 | |String() string | 将值以字符串类型返回 | **演示** ```go package main import ( "fmt" "reflect" ) func main() { var a int = 56 value := reflect.ValueOf(a) fmt.Println(value.Interface()) fmt.Println(value.Interface().(int)) } ``` **结果:** `56` **代码说明:** ```go - 第9行:定义变量a并赋值为56 - 第10行:通过reflect.ValueOf函数获取值对象 - 第11行:获取值对象的原型值 - 第12行:通过断言获取原型值 ``` # 通过ValueOf访问成员对象 反射值对象(reflect.Value)提供对结构体访问的方法,通过这些方法可以完成对结构体任意值的访问,如下表所示。 | 方 法 | 备 注 | |--|--| |Field(i int) Value | 根据索引,返回索引对应的结构体成员字段的反射值对象。当值不是结构体或索引超界时发生宕机 | | NumField() int |返回结构体成员字段数量。当值不是结构体或索引超界时发生宕机 | | FieldByName(name string) Value | 根据给定字符串返回字符串对应的结构体字段。没有找到时返回零值,当值不是结构体或索引超界时发生宕机 | | FieldByIndex(index []int) Value | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的值。 没有找到时返回零值,当值不是结构体或索引超界时发生宕机| | FieldByNameFunc(match func(string) bool) Value | 根据匹配函数匹配需要的字段。找到时返回零值,当值不是结构体或索引超界时发生宕机 | **演示** ```go package main import ( "fmt" "reflect" ) type Turbo struct { Name string Age int } func main() { turbo := &Turbo{ Name: "迈莫coding", Age: 1, } value := reflect.ValueOf(turbo) if value.Kind() == reflect.Ptr { value = value.Elem() for i := 0; i < value.NumField(); i++ { field := value.Field(i) fmt.Printf("字段类型:%v, 字段值:%v\n", field.Type(), field.Interface()) } st := value.FieldByName("Name") fmt.Printf("%v\n", st.Interface()) } } ``` **结果:** ```go 字段类型:string, 字段值:迈莫coding 字段类型:int, 字段值:1 迈莫coding ``` **代码说明:** ```go - 第8行:定义结构体Turbo - 第14行:初始化Turbo结构体对象 - 第18行:通过reflect.ValueOf函数获取Value对象 - 第19行:判断反射类型对象种类是否为指针 - 第20行:获取指针指向的元素 - 第21行:遍历循环结构体中的字段属性 - 第22行:获取某个字段属性的Value对象 - 第23行:获取某个字段类型和字段原值 ``` # 通过canSet方法修改值 一个 reflect.Value 值的 CanSet 方法将返回此 reflect.Value 值代表的Go值是否可以被修改(可以被赋值)。 如果一个Go值可以被修改,则我们可以调用对应的 reflect.Value 值的Set方法来修改此Go值。 注意: reflect.ValueOf 函数直接返回的 reflect.Value 值都是不可修改的。 ## 判定及获取元素的相关方法 使用 reflect.Value 取元素、取地址及修改值的属性方法请参考下表。 | 方法名 | 备 注 | |--|--| | Elem() Value | 取值指向的元素值,类似于语言层*操作。当值类型不是指针或接口时发生宕 机,空指针时返回 nil 的 Value | | Addr() Value | 对可寻址的值返回其地址,类似于语言层&操作。当值不可寻址时发生宕机 | | CanAddr() bool | 表示值是否可寻址 | | CanSet() bool |返回值能否被修改。要求值可寻址且是导出的字段 | ## 值修改相关方法 使用 reflect.Value 修改值的相关方法如下表所示。 | Set(x Value) | 将值设置为传入的反射值对象的值 | |--|--| |Setlnt(x int64) |使用 uint64 设置值。当值的类型不是 uint、uint8、uint16、uint32、uint64 时会发生宕机 | | SetUint(x uint64) | 使用 uint64 设置值。当值的类型不是 uint、uint8、uint16、uint32、uint64 时会发生宕机| | SetFloat(x float64) | 使用 float64 设置值。当值的类型不是 float32、float64 时会发生宕机 | | SetBool(x bool) | 使用 bool 设置值。当值的类型不是 bod 时会发生宕机 | | SetBytes(x []byte) | 设置字节数组 []bytes值。当值的类型不是 []byte 时会发生宕机 | | SetString(x string) | 设置字符串值。当值的类型不是 string 时会发生宕机| ## 值修改条件:可寻址 通过反射修改变量值的前提条件之一:这个值必须可以被寻址。简单地说就是这个变量必须能被修改。 - **可寻址,可被修改** ```go package main import ( "fmt" "reflect" ) func main() { n := "迈莫coding" str := &n value := reflect.ValueOf(str) fmt.Printf("返回值是否被修改?%v, \n表示值是否可寻址?%v\n", value.CanSet(), value.CanAddr()) vn := value.Elem() fmt.Printf("返回值是否被修改?%v, \n表示值是否可寻址?%v\n", value.CanSet(), value.CanAddr()) vn.Set(reflect.ValueOf("迈莫coding正式运营一个月")) fmt.Println(n) } ``` **结果:** ```go 返回值是否被修改?false, 表示值是否可寻址?false 返回值是否被修改?false, 表示值是否可寻址?false 迈莫coding正式运营一个月 ``` **代码说明:** ```go - 第9行:定义字符串n - 第11行:通过reflect.ValueOf函数获取Value对象 - 第13行:取得vp的底层指针值引用的值的代表值 - 第15行:通过set()方法设置新值 ``` - **非指针地址数据不可被修改** ```go package main import ( "fmt" "reflect" ) func main() { var a int = 666 value := reflect.ValueOf(a) value.SetInt(888) fmt.Println(a) } ``` **结果:** ```go panic: reflect: reflect.Value.SetInt using unaddressable value ``` 通过反射对非指针数据进行修改时,会抛出异常。 - **结构体值的非导出字段不能通过反射来修改** ```go package main import ( "fmt" "reflect" ) type Turbo struct { Name interface{} age interface{} } func main() { vs := reflect.ValueOf(&Turbo{}) vs = reflect.Indirect(vs) vx, vy := vs.Field(0), vs.Field(1) fmt.Println(vx.CanSet(), vx.CanAddr()) fmt.Println(vy.CanSet(), vy.CanAddr()) vb := reflect.ValueOf(123) vx.Set(vb) vy.Set(vb) // 会造成恐慌,因为vy代表的值是不可修改的。 fmt.Println(vx.IsNil(), vy.IsNil()) } ``` **结果:** ```go true true false true panic: reflect: reflect.Value.Set using value obtained using unexported field false true ``` **代码说明:** ```go - 第8行:定义一个结构体类型Turbo - 第14行:如果vs代表着一个指针,下一行等价于"vs := vs.Elem()" - 第15行:分别取出结构体对象中的字段属性值 - 第17行:vy为是地址类型但不可被修改 - 第19行:判断vx,vy是否为空值 - 第20行:vx代表的值是可修改的,可暴露的字段 - 第21行:vy代表的值是不可修改的,会Panic异常 ``` # 闲聊 - 读完文章,自己是不是和反射的cp率又提高了 - 我是迈莫,欢迎大家和我交流 > 觉得文章写得不错的小伙伴,点个赞👍 鼓励一下吧~ # 欢迎加入我的公众号【迈莫coding】 一起pk大厂 - **迈莫coding欢迎客官的到来** ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210112120323563.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMDY2MDY2,size_16,color_FFFFFF,t_70#pic_center)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

542 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传