目录
- 概念
- 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 | 将值以字符串类型返回 |
演示
package mainimport ( "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 mainimport ( "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 mainimport ( "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 mainimport ( "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 mainimport ( "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 truefalse truepanic: reflect: reflect.Value.Set using value obtained using unexported fieldfalse true复制代码
代码说明:
- 第8行:定义一个结构体类型Turbo - 第14行:如果vs代表着一个指针,下一行等价于"vs := vs.Elem()"- 第15行:分别取出结构体对象中的字段属性值 - 第17行:vy为是地址类型但不可被修改 - 第19行:判断vx,vy是否为空值 - 第20行:vx代表的值是可修改的,可暴露的字段 - 第21行:vy代表的值是不可修改的,会Panic异常复制代码
闲聊
- 读完文章,自己是不是和反射的cp率又提高了
- 我是迈莫,欢迎大家和我交流
有疑问加站长微信联系(非本文作者)