脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Go语言学习之反射的用法详解

Go语言学习之反射的用法详解

2022-09-26 12:27隐姓埋名4869 Golang

反射指的是运行时动态的获取变量的相关信息。本文将为大家详细介绍Go语言中反射的用法,文中的示例代码讲解详细,感兴趣的可以了解一下

反射指的是运行时动态的获取变量的相关信息

1. reflect 包

类型是变量,类别是常量

reflect.TypeOf,获取变量的类型,返回reflect.Type类型

reflect.ValueOf,获取变量的值,返回reflect.Value类型

reflect.Value.Kind,获取变量的类别,返回一个常量

reflect.Value.Interface(),转换成interface{}类型

Go语言学习之反射的用法详解

1.1 获取变量类型

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
 
import (
    "fmt"
    "reflect"
)
 
func Test(i interface{}) {
    //反射数据类型
    t := reflect.TypeOf(i)
    fmt.Println("类型是", t)
    //反射数据值
    v := reflect.ValueOf(i)
    fmt.Println("值是", v)
}
 
func main() {
    a := "hello"
    Test(a)
}

输出结果如下

类型是 string
值是 hello

?
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
package main
 
import (
    "fmt"
    "reflect"
)
 
type Student struct {
    Name  string
    Age   int
    Score float32
}
 
func Test(i interface{}) {
    //反射获取类型
    t := reflect.TypeOf(i)
    fmt.Println("类型是", t)
 
    //反射获取值
    v := reflect.ValueOf(i)
    //判断值的类别
    c := v.Kind()
    fmt.Println("类别是", c)
}
 
func main() {
    var stu Student = Student{
        Name:  "张三",
        Age:   18,
        Score: 80,
    }
    Test(stu)
 
    fmt.Println("-------------")
    var num int = 10
    Test(num)
}

输出结果如下

类型是 main.Student
类别是 struct
-------------
类型是 int
类别是 int

1.2 断言处理类型转换

?
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
package main
 
import (
    "fmt"
    "reflect"
)
 
type Student struct {
    Name  string
    Age   int
    Score float32
}
 
func Test(i interface{}) {
    t := reflect.TypeOf(i)
    fmt.Println("类型是", t)
 
    //类别
    v := reflect.ValueOf(i)
    c := v.Kind()
    fmt.Println("类别是", c)
    fmt.Printf("c的类型是%T\n", c)
    fmt.Printf("v的类型是%T\n", v)
 
    //转换成接口
    iv := v.Interface()
    fmt.Printf("iv的类型%T\n", iv)
    //断言处理
    stu_iv, err := iv.(Student)
    if err {
        fmt.Printf("stu_iv的类型%T\n", stu_iv)
    }
}
 
func main() {
    var stu Student = Student{
        Name:  "张三",
        Age:   18,
        Score: 80,
    }
    Test(stu)
 
}

输出结果如下

类型是 main.Student
类别是 struct
c的类型是reflect.Kind
v的类型是reflect.Value
iv的类型main.Student
stu_iv的类型main.Student

2. ValueOf

2.1 获取变量值

reflect.valueof(x).Float()

reflect.valueof(x).Int()

reflect.valueof(x).String()

reflect.Valueof(x).Bool()

2.2 类型转换

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
 
import (
    "fmt"
    "reflect"
)
 
func Test(i interface{}) {
    v := reflect.ValueOf(i)
    fmt.Printf("v的类型是%T\n", v)
    //转换成指定类型
    t := v.Int()
    fmt.Printf("t的类型是%T\n", t)
}
 
func main() {
    //类型不同的话会报错
    var num int = 100
    Test(num)
}

输出结果如下

v的类型是reflect.Value
t的类型是int64

3. Value.Set

3.1 设置变量值

reflect.Value.SetFloat(),设置浮点数

reflect.value.SetInt(),设置整数

reflect.Value.SetString(),设置字符串

3.2 示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
 
import (
    "fmt"
    "reflect"
)
 
func Test(i interface{}) {
    v := reflect.ValueOf(i)
    //更新值需要value的地址,否则会保存,Elem()表示指针*
    v.Elem().SetInt(100)
    result := v.Elem().Int()
    fmt.Printf("result类型为 %T, 值为 %d\n", result, result)
}
 
func main() {
    var num int = 10
    Test(&num)
}

输出结果如下

result类型为 int64, 值为 100

4. 结构体反射

反射出结构体的属性和方法数量

方法名需大写,需要被跨包调用识别

4.1 查看结构体字段数量和方法数量

?
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
package main
 
import (
    "fmt"
    "reflect"
)
 
//结构体
type Student struct {
    Name  string
    Age   int
    Score float32
}
 
//结构体方法
func (s Student) Run() {
    fmt.Println("Running")
}
 
func (s Student) Sleep() {
    fmt.Println("Sleeping")
}
 
//使用反射查看结构体字段数量和方法数量
func Test(i interface{}) {
    v := reflect.ValueOf(i)
    //类别判断
    if v.Kind() != reflect.Struct {
        fmt.Println("不是结构体")
        return
    }
    //获取结构体中字段数量
    stu_num := v.NumField()
    fmt.Println("字段数量: ", stu_num)
    //获取结构体中方法数量
    stu_meth := v.NumMethod()
    fmt.Println("方法数量: ", stu_meth)
}
 
func main() {
    var stu Student = Student{
        Name:  "张三",
        Age:   18,
        Score: 88,
    }
    Test(stu)
}

输出结果如下

字段数量:  3
方法数量:  2

4.2 获取结构体属性

?
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
package main
 
import (
    "fmt"
    "reflect"
)
 
type Student struct {
    Name  string
    Age   int
    Score float32
}
 
func Test(i interface{}) {
    v := reflect.ValueOf(i)
    //获取结构体中每个属性
    for i := 0; i < v.NumField(); i++ {
        //输出属性值
        fmt.Printf("%d %v\n", i, v.Field(i))
        //输出属性值的类型
        fmt.Printf("%d %v\n", i, v.Field(i).Kind())
    }
}
 
func main() {
    var stu Student = Student{
        Name:  "张三",
        Age:   18,
        Score: 88,
    }
    Test(stu)
}

输出结果如下

0 张三
0 string
1 18
1 int
2 88
2 float32

4.3 更改属性值

?
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
package main
 
import (
    "fmt"
    "reflect"
)
 
type Student struct {
    Name  string
    Age   int
    Score float32
}
 
func Test(i interface{}, name string) {
    v := reflect.ValueOf(i)
    vk := v.Kind()
    //判断是都为指针并指向结构体类型
    if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct {
        fmt.Println("expect struct")
        return
    }
    //更改属性值
    v.Elem().Field(0).SetString(name)
    //获取结构体中每个属性
    for i := 0; i < v.Elem().NumField(); i++ {
        //输出属性值
        fmt.Printf("%d %v\n", i, v.Elem().Field(i))
        //输出属性值的类型
        fmt.Printf("%d %v\n", i, v.Elem().Field(i).Kind())
    }
}
 
func main() {
    var stu Student = Student{
        Name:  "张三",
        Age:   18,
        Score: 88,
    }
    Test(&stu, "李四")
}

输出结果如下

0 李四
0 string
1 18
1 int
2 88
2 float32

4.4 Tag原信息处理

?
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
package main
 
import (
    "encoding/json"
    "fmt"
    "reflect"
)
 
type Student struct {
    Name  string `json:"stu_name"`
    Age   int
    Score float32
}
 
func Test(i interface{}, name string) {
    v := reflect.ValueOf(i)
    vk := v.Kind()
    //判断是都为指针并指向结构体类型
    if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct {
        fmt.Println("expect struct")
        return
    }
    //更改属性值
    v.Elem().Field(0).SetString(name)
    //获取结构体中每个属性
    for i := 0; i < v.Elem().NumField(); i++ {
        //输出属性值
        fmt.Printf("%d %v\n", i, v.Elem().Field(i))
        //输出属性值的类型
        fmt.Printf("%d %v\n", i, v.Elem().Field(i).Kind())
    }
}
 
func main() {
    var stu Student = Student{
        Name:  "张三",
        Age:   18,
        Score: 88,
    }
    Test(&stu, "李四")
    fmt.Println("----------------json原信息----------------")
    result, _ := json.Marshal(stu)
    fmt.Println("json原信息: ", string(result))
    //反射获取类型
    st := reflect.TypeOf(stu)
    s := st.Field(0)
    fmt.Printf("Name原信息名称: %s\n", s.Tag.Get("json"))
}

输出结果如下

0 李四
0 string
1 18
1 int
2 88
2 float32
----------------json原信息----------------
json原信息:  {"stu_name":"李四","Age":18,"Score":88}
Name原信息名称: stu_name

5. 函数反射

Go 中函数是可以赋值给变量的

示例:

既然函数可以像普通的类型变量一样,那么在反射机制中就和不同的变量是一样的,在反射中函数和方法的类型(Type)都是reflect.Func,如果要调用函数,通过 Value 的Call() 方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
 
import (
    "fmt"
    "reflect"
)
 
func hello() {
    fmt.Println("hello world")
}
 
func main() {
    //反射使用函数
    v := reflect.ValueOf(hello)
    //类型判断是否属于reflect.func类型
    if v.Kind() == reflect.Func {
        fmt.Println("函数")
    }
    //反射调用函数
    v.Call(nil)   //Call中需要传入的是切片
}

输出结果如下

函数
hello world

?
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
package main
 
import (
    "fmt"
    "reflect"
    "strconv"
)
 
//反射调用传参和返回值函数
func Test(i int) string {
    return strconv.Itoa(i)
}
 
func main() {
    v := reflect.ValueOf(Test)
    //定义参数切片
    params := make([]reflect.Value, 1)
    //切片元素赋值
    params[0] = reflect.ValueOf(20)
    //反射调函数
    result := v.Call(params)
    fmt.Printf("result的类型是 %T\n", result)
    //[]reflect.Value切片转换string
    s := result[0].Interface().(string)
    fmt.Printf("s的类型是 %T ,值为 %s\n", s, s)
}

输出结果如下

result的类型是 []reflect.Value
s的类型是 string ,值为 20

6. 方法反射

反射中方法的调用,函数和方法可以说其实本质上是相同的,只不过方法与一个“对象”进行了“绑定”,方法是“对象”的一种行为,这种行为是对于这个“对象”的一系列操作,例如修改“对象”的某个属性

6.1 使用 MethodByName 名称调用方法

?
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
package main
 
import (
    "fmt"
    "reflect"
    "strconv"
)
 
//反射方法
type Student struct {
    Name string
    Age  int
}
 
//结构体方法
func (s *Student) SetName(name string) {
    s.Name = name
}
 
func (s *Student) SetAge(age int) {
    s.Age = age
}
 
func (s *Student) String() string {
    return fmt.Sprintf("%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age)
}
 
func main() {
    //实例化
    stu := &Student{"张三", 19}
    //反射获取值:指针方式
    stuV := reflect.ValueOf(&stu).Elem()
    fmt.Println("修改前: ", stuV.MethodByName("String").Call(nil)[0])
    //修改值
    params := make([]reflect.Value, 1)      //定义切片
    params[0] = reflect.ValueOf("李四")
    stuV.MethodByName("SetName").Call(params)
    params[0] = reflect.ValueOf(20)
    stuV.MethodByName("SetAge").Call(params)
    fmt.Println("修改后: ", stuV.MethodByName("String").Call(nil)[0])
}

输出结果如下

修改前:  0xc000004078,Name:张三,Age:19
修改后:  0xc000004078,Name:李四,Age:20

6.2 使用 method 索引调用方法

?
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
package main
 
import (
    "fmt"
    "reflect"
    "strconv"
)
 
//反射方法
type Student struct {
    Name string
    Age  int
}
 
//结构体方法
func (s *Student) B(name string) {
    s.Name = name
}
 
func (s *Student) A(age int) {
    s.Age = age
}
 
func (s *Student) C() string {
    return fmt.Sprintf("%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age)
}
 
func main() {
    //实例化
    stu := &Student{"张三", 19}
    //反射获取值:指针方式
    stuV := reflect.ValueOf(&stu).Elem()
 
    //索引调用方法
    fmt.Println("修改前: ", stuV.Method(2).Call(nil)[0])
 
    params := make([]reflect.Value, 1)
    params[0] = reflect.ValueOf("李四")
    stuV.Method(1).Call(params)
 
    params[0] = reflect.ValueOf(20)
    stuV.Method(0).Call(params)
    fmt.Println("修改后: ", stuV.Method(2).Call(nil)[0])
    //调用索引大小取决于方法名称的ASCII大小进行排序
}

输出结果如下

修改前:  0xc000004078,Name:张三,Age:19
修改后:  0xc000004078,Name:李四,Age:20

到此这篇关于Go语言学习之反射的用法详解的文章就介绍到这了,更多相关Go语言反射内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/lvrui/p/16159478.html

延伸 · 阅读

精彩推荐
  • Golanggolang开发及数字证书研究分享

    golang开发及数字证书研究分享

    这篇文章主要为大家介绍了golang开发以及数字证书的研究示例分享,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步...

    秋天的春6102021-12-03
  • Golang浅谈golang结构体偷懒初始化

    浅谈golang结构体偷懒初始化

    这篇文章主要介绍了浅谈golang结构体偷懒初始化,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    渔舟唱晚_hanpan12692021-03-08
  • Golang在Golang代码中如何自动生成版本号的方法示例

    在Golang代码中如何自动生成版本号的方法示例

    这篇文章主要给大家介绍了在Golang代码中如何自动生成版本号的相关资料,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋...

    andy zhang7402020-05-07
  • Golang用 Go struct 不能犯的一个低级错误!

    用 Go struct 不能犯的一个低级错误!

    前段时间我分享了 《手撕 Go 面试官:Go 结构体是否可以比较,为什么?》的文章,把基本 Go struct 的比较依据研究了一番。这不,最近有一位读者,遇到了...

    脑子进煎鱼了11102021-04-16
  • GolangGo语言Telnet回音服务器的实现

    Go语言Telnet回音服务器的实现

    这篇文章主要介绍了Go语言Telnet回音服务器的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下...

    huang_he_6372020-06-02
  • GolangGolang简单实现http的server端和client端

    Golang简单实现http的server端和client端

    Http 服务是基于 Tcp 的应用层的实现,也是最常见的网络协议之一。本文主要介绍了Golang简单实现http的server端和client端,感兴趣的可以了解一下...

    锐玩道7832021-08-06
  • Golanggolang中xorm的基本使用说明

    golang中xorm的基本使用说明

    这篇文章主要介绍了golang中xorm的基本使用说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    yí無所冇10302021-03-09
  • Golangwin10下go mod配置方式

    win10下go mod配置方式

    这篇文章主要介绍了win10下go mod配置方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    ytd77778702021-05-29