golang:package reflect简单了解(1)

reflect大概

类型与方法

通过go doc reflect包可以发现其中我们最常用的一个type Type interface和一个type Value struct

通过以下两个方法可以获取到他们

1
2
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value

可以看出在使用上面两个方法的时候是有将参数做空接口处理转换的。

reflect.Type是指的变量的类型。其中reflect.Type规定了很多的方法。
reflect.Value是指的变量值的相关信息.并提供了方法供我们使用查询。

具体使用

reflect.Type

首先是reflect.Type。最简单的自然是获取目标的类型。上面的TypeOf就可以直接返回目标的类型。但是其返回的是最浅层的类型。相当与var type x
他返回的就是type。申明的什么返回什么。

1
2
type sepString string
var s sepString = "abc"

这时候:
reflect.TypeOf返回的就是sepString.当然前缀是你定义type的包名。
如果想要获取其底层的数据类型则需要使用Kind()方法
reflect.TypeOf(s).Kind()返回的就是string.

对于struct其返回的自然是定义的结构提名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import (
"fmt"
"reflect"
)
type testStruct struct {
name string
Num int
}
func main() {
v := testStruct{name: "test", Num: 1}
fmt.Println(reflect.TypeOf(v)) //main.testStruct
}

reflect.Value

通过reflect.ValueOf返回该值。其表示了目标的值。在reflect包中为其提供了众多的方法供我们使用。部分方法如下说明。

Type()返回reflect.Value的Type。同样返回的是申明类型。参考reflect.TypeOf

Kind()返回底层的类型

interface()返回还原值

Elem()返回interface或者pointer的值。

其中Elem()涉及到通过反射修改目标值。首先修改目标值必须是修改其本身,就和interface的实现一样。判断是否实现接口时对于reciever是否是指针类型是十分严格的。

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
/**
Elem returns the value that the interface v contains or that the pointer v
points to. It panics if v's Kind is not Interface or Ptr. It returns the
zero Value if v is nil.
*/
//func (v Value) Elem() Value
package main
import(
"fmt"
"reflect"
)
func main(){
type st string
var s st = "abc"
v := reflect.ValueOf(s)
v2 := reflect.ValueOf(&s)//通过指针获取其本身。地址获取
fmt.Println(v.CanSet()) //false
//fmt.Println(v.Elem().CanSet())
fmt.Println(v2.CanSet()) //false
fmt.Println(v2.Elem().CanSet()) //true
v2.Elem().SetString("cba")
fmt.Println(s)//cba
}

就像上面代码写的一样,主要是通过指针获取。还有就是通过reflect.Value.Elem()方法获取reflect.Value的值。最上面的Elem注释是通过go doc 查询所得

对于 struct

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main
import (
"fmt"
"reflect"
"strconv"
)
type testStruct struct {
name string
Num int
}
func (t testStruct) String() string {
return t.name + " and " + strconv.Itoa(t.Num)
}
func (t testStruct) ignore() string {
return t.name
}
func main() {
v := testStruct{name: "test", Num: 1}
value := reflect.ValueOf(v)
fmt.Println(value)//{test 1}
//获取struct的成员数量
count := value.NumField()
fmt.Println(count) //2
//通过Field(i)获取指定序列的值
for i := 0; i < count; i++ {
fmt.Printf("Field %d is %v\n", i, value.Field(i))
}
//Field 0 is test
//Field 1 is 1
//通过反射修改struct的field值.同样的需要指针寻址
value2 := reflect.ValueOf(&v)
fmt.Println(value2)//&test and 1
value21 := value2.Elem()
fmt.Println(value21.CanSet()) //true
//value21.FieldByName("name").SetString("notTest") //panic 可导出才可以修改
//通过Field(i int).setInt()来修改制定序列的field的值
value21.Field(1).SetInt(2)
fmt.Println(v.Num) //2
//获取struct的Method数量
fmt.Println(value.NumMethod()) //1 仅能获取可导出的方法计数
//通过reflect.Value.Method(i int).Call(args)来调用指定序列的方法。
fmt.Println(value.Method(0).Call(nil))//[test and 1]
//通过reflect.Value.MethodByName(name string).Call(args)来调用指定名称的方法。
fmt.Println(value.MethodByName("String").Call(nil))//[test and 1]
//fmt.Println(value.MethodByName("ignore").Call(nil)) //panic 可导出方法才可以
}

这里基本就在上面的代码写的差不多了。还有就是最后Method.Call为什么显示的是[test and 1]的问题。这也是为什么说修改时是使用指针寻址来的原因。上面value=reflect.ValueOf(v)其实是产生的一个副本给value。所以修改了v的本身并不会影响value。

差不多简单了解了这么多。因时间以及能力原因我只是简单测试所得。其中估计会有不恰当或者错误的地方。在之后的学习中如果有发现会后续补充。如果有看到的同学还是要多实践以及阅读源码。

客官扫码领红包哟~