目录
- 概念
- ValueOf使用格式
- 反射获取值对象(ValueOf)
- 通过canSet方法修改值
- 判断及获取元素的相关方法
- 值修改相关方法
- 值修改条件:可寻址
- 结构体值的非导出字段不能通过反射来修改
- 通过ValueOf访问成员对象
- 闲聊
- 【迈莫coding】
概念
在程序运行期间,如果想要获取某个接口所代表的值,那么可以使用 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 | 将值以字符串类型返回 |
演示
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
代码说明:
- 第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 | 根据匹配函数匹配需要的字段。找到时返回零值,当值不是结构体或索引超界时发生宕机 |
演示
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())
}
}
结果:
字段类型:string, 字段值:迈莫coding
字段类型:int, 字段值:1
迈莫coding
代码说明:
- 第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 时会发生宕机 |
值修改条件:可寻址
通过反射修改变量值的前提条件之一:这个值必须可以被寻址。简单地说就是这个变量必须能被修改。
- 可寻址,可被修改
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)
}
结果:
返回值是否被修改?false,
表示值是否可寻址?false
返回值是否被修改?false,
表示值是否可寻址?false
迈莫coding正式运营一个月
代码说明:
- 第9行:定义字符串n
- 第11行:通过reflect.ValueOf函数获取Value对象
- 第13行:取得vp的底层指针值引用的值的代表值
- 第15行:通过set()方法设置新值
- 非指针地址数据不可被修改
package main
import (
"fmt"
"reflect"
)
func main() {
var a int = 666
value := reflect.ValueOf(a)
value.SetInt(888)
fmt.Println(a)
}
结果:
panic: reflect: reflect.Value.SetInt using unaddressable value
通过反射对非指针数据进行修改时,会抛出异常。
- 结构体值的非导出字段不能通过反射来修改****加粗样式
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())
}
结果:
true true
false true
panic: reflect: reflect.Value.Set using value obtained using unexported field
false true
代码说明:
- 第8行:定义一个结构体类型Turbo
- 第14行:如果vs代表着一个指针,下一行等价于"vs := vs.Elem()"
- 第15行:分别取出结构体对象中的字段属性值
- 第17行:vy为是地址类型但不可被修改
- 第19行:判断vx,vy是否为空值
- 第20行:vx代表的值是可修改的,可暴露的字段
- 第21行:vy代表的值是不可修改的,会Panic异常
闲聊
- 读完文章,自己是不是和反射的cp率又提高了
- 我是迈莫,欢迎大家和我交流
觉得文章写得不错的小伙伴,点个赞👍 鼓励一下吧~
文章也会持续更新,可以微信搜索「 迈莫coding 」第一时间阅读。
网友评论