声明:文章内容取自雨痕老师《Go语言学习笔记》

和Type获取类型信息不同,Value专注于对象实例数据读写。

在前面章节曾提到过,接口变量会复制对象,且是unaddressable的,所以要想修改目标对象,就必须使用指针。

package main

import (
"fmt"
"reflect"
) func main() {
a := 100
va, vp := reflect.ValueOf(a), reflect.ValueOf(&a).Elem()
fmt.Println(va.CanAddr(), va.CanSet())
fmt.Println(vp.CanAddr(), vp.CanSet())
}

输出:

false false
true true

就算传入指针,一样需要通过Elem获取目标对象。因为被接口存储的指针本身是不能寻址和进行设置操作的。


注意,不能对非导出字段直接进行设置操作,无论是当前包还是外包。
```golang
package main

import (

"fmt"

"reflect"

"unsafe"

)

type User struct {

Name string

code int

}

func main() {

p := new(User)

v := reflect.ValueOf(p).Elem()

name := v.FieldByName("Name")

code := v.FieldByName("code")

fmt.Printf("name:canaddr = %v,canset = %v\n", name.CanAddr(), name.CanSet())

fmt.Printf("code:canaddr = %v,canset = %v\n", code.CanAddr(), code.CanSet())

if name.CanSet() {

name.SetString("Tom")

}

if code.CanAddr() {

(int)(unsafe.Pointer(code.UnsafeAddr())) = 100

}

fmt.Printf("%+v\n", *p)

}

输出:

name:canaddr = true,canset = true

code:canaddr = true,canset = false

{Name:Tom code:100}

<hr>
Value.Pointer和Value.Int等方法类似,将Value.data存储的数据转换为指针,目标必须是指针类型。而UnsafeAddr返回任何CanAddr Value.data地址(相当于&取地址操作),比如Elem后的Value,以及字段成员地址。
以结构体里的指针类型字段为例,Pointer返回该字段所保存的地址,而UnsafeAddr返回该字段自身的地址(结构对象地址+偏移量)
<hr>
可通过Interface方法进行类型推断和转换。
```golang
package main import (
"fmt"
"reflect"
) type user struct {
Name string
Age int
} func main() {
u := user{
"雨痕",
60, //如果最后一个字段不和}相连,则必须加上,
}
v := reflect.ValueOf(&u)
if !v.CanInterface() {
fmt.Println("CanInterfae:fail.")
return
}
p, ok := v.Interface().(*user)
if !ok {
fmt.Println("Interface:fail.")
return
}
p.Age++
fmt.Printf("%+v\n", u)
}

输出:

{Name:雨痕 Age:61}

也可直接使用Value.Int、Bool等方法进行类型转换,但失败时会引发panic,且不支持ok-idiom。


复合类型对象设置示例:
```golang
package main

import (

"fmt"

"reflect"

)

func main() {

c := make(chan int, 4)

v := reflect.ValueOf(c)

if v.TrySend(reflect.ValueOf(100)) {

fmt.Println(v.TryRecv())

}

}

输出:

100 true

接口有两种nil状态,这一直是个潜在麻烦。解决方法是用IsNil判断值是否为nil。
```golang
package main import (
"fmt"
"reflect"
) func main() {
var a interface{} = nil
var b interface{} = (*int)(nil)
fmt.Println(a == nil)
fmt.Println(b == nil, reflect.ValueOf(b).IsNil())
}

输出:

true
false true

也可用unsafe转换后直接判断iface.data是否为零值。

package main

import (
"fmt"
"unsafe"
) func main() {
var b interface{} = (*int)(nil)
iface := (*[2]uintptr)(unsafe.Pointer(&b))
fmt.Println(iface, iface[1] == 0)
}

输出:

&[4785664 0] true

让人很无奈的是,Value里的某些方法并未实现ok-idom或返回error,所以得自行判断返回的是否为Zero Value。

package main

import (
"fmt"
"reflect"
) func main() {
v := reflect.ValueOf(struct{ name string }{})
fmt.Println(v.FieldByName("name").IsValid())
fmt.Println(v.FieldByName("xxx").IsValid())
}

输出:

true
false

最新文章

  1. 从零开始编写自己的C#框架(13)——T4模板在逻辑层中的应用(二)
  2. Wireshark查看https的通讯
  3. UI进阶 跳转系统设置相关界面的方法
  4. 运用requirejs的异步加载方式
  5. 不用插件 让Firefox 支持网页翻译
  6. 004商城项目:ssm框架的整合之后的调试
  7. python迭代器与iter()函数实例教程
  8. mysql系统库INFORMATION_SCHEMA,MySQL,TEST,mysql系统表的作用
  9. java版本的sqlHelper
  10. ESB的XmlProPertyMgr类的getNode(xxx)方法
  11. HDOJ1021题 Fibonacci Again 应用求模公式
  12. CC版本添加 LUCI
  13. weighted_cross_entropy_with_logits
  14. 学习 day4 html 盒子模型
  15. swagger2常用注解
  16. http/https协议
  17. 项目集成swagger【转载】
  18. 解析.NET对象的跨应用程序域访问(上篇)
  19. [Educational Round 3][Codeforces 609E. Minimum spanning tree for each edge]
  20. OpenCV在字符提取中进行的预处理(转)

热门文章

  1. 鸟哥的linux私房菜服务器架设篇第五章linux常用网络指令
  2. Shell--nl命令
  3. min-height IE6的解决方案
  4. Docker核心技术
  5. Docker解析及轻量级PaaS平台演练(一)--Docker简介与安装
  6. Coincidence (动态规划求最长公共子序列)(王道)
  7. Centos 7 进入单用户模式图文详解
  8. ELK学习笔记
  9. NYOJ1097 Ugly Numbers 【丑数】
  10. activiti designer下载地址