golang学习笔记---reflect包

摘要:
所以我们引入了reflect包。1234567891011121314151617packagemainimportfuncreflectType{v:=reflect.TypeOffmt.Printf}funcmain(){varafloat32=3.14reflectType//type:float32varbint64=100reflectType//type:int64}ValueOf ValueOf函数可以接收任意的interface{}并将接口的动态值以reflect.Value的形式返回。与reflect.TypeOf类似,reflect.ValueOf的返回值也是具体值,不过reflect.Value也可以包含一个接口值。对于不同类型,我们用reflect.Value的kind方法来区分不同类型。reflect.Value的零值就属于Invalid类型。
go语言提供了一种机制,在编译时不知道类型的情况下,可更新变量,在运行时查看值,调用方法以及直接对他们的布局进行操作。这种机制称为反射(reflection)。

为什么使用反射

有时候我们需要写一个函数有能力统一处理各种值类型的函数,而这些类型可能无法共享同一个接口,也可能布局未知,也有可能这个类型在我们设计函数时还不存在。甚至这个类会同时存在上面三个问题。假设我们设计一个例子来判断,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
packagemain
import"fmt"
typestringerinterface{
Stringer() string
}
funcSprint(xinterface{}) {
switchx := x.(type) {
casestringer:
fmt.Println(x,"is stringer")
casestring:
fmt.Println(x,"is string")
caseint:
fmt.Println("int")
default:
fmt.Println("其他类型")
}
}
funcmain() {
Sprint("fd")
}

这时你会发现,如果类型很多或者更多不确定的类型就会很麻烦。所以我们引入了reflect包。

reflect

反射功能由reflect包提供,它定义了两个重要的类型:Type和Value,并且TypeOf和ValueOf

  • Type: 这是一个接口,真正使用该接口的实例是reflect.rtype,该实例持有动态类型的所有信息。并且提供下面方法
    • kind(): 获取具体类型的底层类型。
    • Elem(): 这个方法返回的是原始变量的元素的类型。
  • Value: 这是一个struct,持有动态值的所有信息。
    • Type(): Type方法返回接口变量的动态类型信息,也就是传入ValueOf方法的原始变量的类型。
    • Kind(): 与Type的kind方法一样,返回的是原始类型。
    • Interface():把一个reflect.Value对象还原回一个空接口类型的变量,可以通过类型断言:x, ok := v.Interface().(int) 
    • Elem(): 调用该方法的Value对象

type.kind() &type.name()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
packagemain
import(
"fmt"
"reflect"
)
typemyInt int64
funcreflectType(xinterface{}) {
t := reflect.TypeOf(x)
fmt.Printf("type:%v kind:%v ", t.Name(), t.Kind())
}
funcmain() {
vara *float32// 指针
varb myInt// 自定义类型
varc rune// 类型别名//代表int32
reflectType(a)// type: kind:ptr
reflectType(b)// type:myInt kind:int64
reflectType(c)// type:int32 kind:int32
typepersonstruct{
name string
age int
}
vard = person{
name:"wang",
age: 18,
}
reflectType(d)// type:person kind:struct
}

TypeOf

TypeOf函数接收任何的interface{}参数,并且把接口中的动态类型以reflect.Type形式返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
packagemain
import(
"fmt"
"reflect"
)
funcreflectType(xinterface{}) {
v := reflect.TypeOf(x)
fmt.Printf("type:%v ", v)
}
funcmain() {
vara float32 = 3.14
reflectType(a)// type:float32
varb int64 = 100
reflectType(b)// type:int64
}

ValueOf 

ValueOf函数可以接收任意的interface{}并将接口的动态值以reflect.Value的形式返回。与reflect.TypeOf类似,reflect.ValueOf的返回值也是具体值,不过reflect.Value也可以包含一个接口值。

对于不同类型,我们用reflect.Value的kind方法来区分不同类型。但类型的分类(kind)只有少数几种:

  • 基础类型: Bool, String以及数字类型
  • 聚合类型:Array, struct
  • 引用类型:Chan, Func, Ptr, Slice, Map
  • 接口类型:interface
  • Invalid类型:表示没有任何值。reflect.Value的零值就属于Invalid类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
packagemain
import(
"fmt"
"reflect"
)
funcreflectValue(xinterface{}) {
v := reflect.ValueOf(x)
k := v.Kind()
switchk {
casereflect.Int64:
// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
fmt.Printf("type is int64, value is %d ", int64(v.Int()))
casereflect.Float32:
// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
fmt.Printf("type is float32, value is %f ", float32(v.Float()))
casereflect.Float64:
// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
fmt.Printf("type is float64, value is %f ", float64(v.Float()))
}
}
funcmain() {
vara float32 = 3.14
varb int64 = 100
reflectValue(a)// type is float32, value is 3.140000
reflectValue(b)// type is int64, value is 100
// 将int类型的原始值转换为reflect.Value类型
c := reflect.ValueOf(10)
fmt.Printf("type c :%T ", c)// type c :reflect.Value
} 

value.Elem设置值

上面知识获取了值及类型,如果想要修改,可以通过elem,但必须传递的是指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
packagemain
import(
"fmt"
"reflect"
)
funcreflectSetValue2(xinterface{}) {
v := reflect.ValueOf(x)
// 反射中使用 Elem()方法获取指针对应的值
ifv.Elem().Kind() == reflect.Int64 {
v.Elem().SetInt(200)
}
}
funcmain() {
vara int64 = 100
reflectSetValue2(&a)
fmt.Println(a)
}

最后整理一下常用的类型判断,如 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
funcDis(path string, v reflect.Value) {
switchv.Kind() {
casereflect.Invalid:// 空
fmt.Printf("%s= Invalid ", path)
casereflect.Slice, reflect.Array:
fori := 0; i<v.Len(); i++{
Dis(fmt.Printf("%s[%d]", path, i), v.Index(i))
}
casereflect.Struct:
fori := 0; i< v.NumField(); i++{
feildPath := fmt.Sprintf("%s.%s",path, v.Type().Field(i).Name)
Dis(feildPath, v.Field(i))
}
casereflect.Map:
for_, key :=rangev.MapKeys(){
fmt.Printf("%s", v.MapIndex(key))
}
casereflect.Ptr:
ifv.IsNil(){
fmt.Printf("%s= nil ", path)
}else{
Dis(fmt.Sprintf("*%s",path),v.Elem())
}
casereflect.Interface:
ifv.IsNil(){
fmt.Printf("%s=nil ", path)
}else{
fmt.Printf("%s.type=%s ",path, v.Elem().Type())
}
default:// 基础类型,chan, 函数
fmt.Printf("%s ",path)
}
}

免责声明:文章转载自《golang学习笔记---reflect包》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇revit 碰撞检测相关2 模拟登录_Post表单方式(针对chinaunix有效,针对csdn失效,并说明原因)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Golang 的 go mod 管理包

go 1.14.4 初始化项目 mod管理包   go mod init example    可能会报如下错误:  go mod init: modules disabled by GO111MODULE=off; see 'go help modules'   解决   set GO111MODULE=on //window 得用管理员才能更改  ...

忽略大小写获取对象属性

如果想获取对象的属性,一般通过函数Type.GetProperty(string, BindingFlags)来完成。其中BindingFlags指定了反射搜索的范围。今天由于需要,决定把原来的区分大小写的获取属性方式,改为不区分大小写,于是把原来的代码 PropertyInfo propInfo = typeof().GetProperty(name)...

macbook golang的debug模式不好使

因为新版mac运用了m1芯片,所以golang在debug的时候会有报错,具体如下: can not run under rosetta check that the installed build of go is right for your cpu architecture 就是cpu架构的问题,所以我们安装arm架构的golang版本即可。具体版本...

golang GET 出现 x509: certificate signed by unknown authority

我们编写一个Go程序来尝试与这个HTTPS server建立连接并通信。 //gohttps/4-https/client1.gopackage main import ("fmt""io/ioutil""net/http") func main() {resp, err := http.Get("https://localhost:8081")if er...

Golang的XML处理

前言 前往https://studygolang.com/pkgdoc,了解golang语言中xml包的内容。文中的内容主要来自于该网站。 XML生成 理论 func Marshal(v interface{}) ([]byte, error) func MarshalIndent(v interface{}, prefix, indent string)...

Golang(三)Goroutine原理

前言 最近用到了一些 Golang 异步编程的地方,感觉 Golang 相对于其他语言(如 Java)对多线程编程的支持非常大,使用起来也非常方便。于是决定了解一下 Goroutine 的底层原理。 Goroutine 本质是协程,是实现并行计算的核心。只需要在对应的函数前加上 Go 关键词即可异步执行: go func() { }() 基本概念...