1.反射:

定义: 反射就是程序能够在运行时检查变量和值,求出它们的类型。
                   可以在运行时动态获取变量的相关信息
                   Import ("reflect")

为什么需要反射?
       想象下:如果程序中每个变量都是我们自己定义的,那么在编译时就可以知道变量类型了,但是实际中并非如此,就需要我们在运行时检查变量,求出它的类型。这就需要用到反射。

在 Go 语言中,reflect 实现了运行时反射。reflect 包会帮助识别 interface{} 变量的底层具体类型和具体值。

几个函数:
a. reflect.TypeOf(val),获取变量的类型,返回reflect.Type类型
b. reflect.ValueOf(val),获取变量的值,返回reflect.Value类型
c. reflect.Value.Kind(),获取变量的类别,返回一个常量
d. reflect.Value.Interface(),转换成interface{}类型

变量接口及获取变量值之间的转换:

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 type Student struct {
 9     Name  string
10     Age   int
11     Score float32
12 }
13
14 func test(b interface{}) {
15     t := reflect.TypeOf(b)
16     fmt.Println("t: ", t)  //t:  main.Student
17
18     v := reflect.ValueOf(b)
19     fmt.Println("v: ", v)  //v:  {stu01 18 92}
20
21     k := v.Kind()
22     fmt.Println("k: ", k)  //k:  struct
23
24     iv := v.Interface()
25     fmt.Println("iv: ", iv)  //iv:  {stu01 18 92}
26     stu, ok := iv.(Student)
27     if ok {
28         fmt.Printf("%v %T\n", stu, stu)  //{stu01 18 92} main.Student
29     }
30 }
31
32 func main() {
33     var a Student = Student{
34         Name:  "stu01",
35         Age:   18,
36         Score: 92,
37     }
38     test(a)
39 }

example

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 func main() {
 9
10     var x float64 = 3.4
11     fmt.Println("type:", reflect.TypeOf(x))  //type: float64
12     v := reflect.ValueOf(x)
13     fmt.Printf("value:%v, type:%T\n", v, v)  //value:3.4, type: reflect.Valuetype
14     fmt.Println("type:", v.Type())  //type: float64
15     fmt.Println("kind:", v.Kind())  //kind: float64
16     fmt.Println("value:", v.Float())  //value: 3.4
17
18     fmt.Println(v.Interface())  //3.4
19     fmt.Printf("value is %5.2e\n", v.Interface())  //value is 3.40e+00
20     y := v.Interface().(float64)  //v -> Interface -> float64 -> y
21     fmt.Println(y)  //3.4
22 }

example2

2. reflect.Value.Kind()方法返回的常量

 1 const (
 2     Invalid Kind = iota
 3     Bool
 4     Int
 5     Int8
 6     Int16
 7     Int32
 8     Int64
 9     Uint
10     Uint8
11     Uint16
12     Uint32
13     Uint64
14     Uintptr
15     Float32
16     Float64
17     Complex64
18     Complex128
19     Array
20     Chan
21     Func
22     Interface
23     Map
24     Ptr
25     Slice
26     String
27     Struct
28     UnsafePointer
29 ) 

Kind返回的常量

3. 获取变量的值:

reflect.ValueOf(x).Float()
reflect.ValueOf(x).Int()
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()

4. 通过反射的来改变变量的值
    reflect.Value.SetXX相关方法,比如:

reflect.Value.SetFloat(),设置浮点数
reflect.Value.SetInt(),设置整数
reflect.Value.SetString(),设置字符串

练习:(panic: reflect: reflect.Value.SetFloat using unaddressable value)

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 func main() {
 9     var a float64
10     fv := reflect.ValueOf(a)
11     fv.SetFloat(3.3)
12     fmt.Printf("%v\n", a)
13 }

程序崩溃了

崩溃的原因:还是值类型和引用类型的原因。v := reflect.ValueOf(x) ,v是x的一个拷贝,修改v,x不会修改!

解决方法:传地址!

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 func main() {
 9     var a float64 = 1.0
10
11     fv := reflect.ValueOf(&a)
12     fv.Elem().SetFloat(3.3)   //相当于var *fv float64;  *fv = 3.3
13     fmt.Printf("%v\n", a) //3.3
14 }

传地址

其中fv.Elem().Setxx用来获取指针指向的变量,相当于:
       var a *int;
       *a = 100

5. 用反射操作结构体
    a. reflect.Value.NumField() 获取结构体中字段的个数
    b. reflect.Value.Method(n).Call 来调用结构体中的方法

 1 func (Value) Call
 2 func (v Value) Call(in []Value) []Value
 3 Call calls the function v with the input arguments in. For example, if len(in) == 3, v.Call(in) represents the Go call v(in[0], in[1], in[2]). Call panics if v's Kind is not Func. It returns the output results as Values. As in Go, each input argument must be assignable to the type of the function's corresponding input parameter. If v is a variadic function, Call creates the variadic slice parameter itself, copying in the corresponding values.
 4
 5 func (Value) Elem
 6 func (v Value) Elem() Value
 7 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.
 8
 9 func (Value) NumField
10 func (v Value) NumField() int
11 NumField returns the number of fields in the struct v. It panics if v's Kind is not Struct.

Elem, NumField,Call官方解释

 1 package main
 2
 3 import (
 4     "encoding/json"
 5     "fmt"
 6     "reflect"
 7 )
 8
 9 type Student struct {
10     Name  string `json:"student_name"`
11     Age   int
12     Score float32
13     Sex   string
14 }
15
16 func (s Student) Print() {
17     fmt.Println("---start----")
18     fmt.Println(s)
19     fmt.Println("---end----")
20 }
21
22 func (s Student) Set(name string, age int, score float32, sex string) {
23     s.Name = name
24     s.Age = age
25     s.Score = score
26     s.Sex = sex
27     fmt.Println("set -----------")
28 }
29
30 func TestStruct(a interface{}) {
31     tye := reflect.TypeOf(a)
32     val := reflect.ValueOf(a)
33     kd := val.Kind()
34     // fmt.Println(tye, val, kd, val.Elem().Kind()) //*main.Student &{stu01 18 92.8 } ptr struct
35
36     if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct {
37         fmt.Println("expect struct")
38         return
39     }
40
41     num := val.Elem().NumField()  //获取val
42     val.Elem().Field(0).SetString("stu1000")
43     for i := 0; i < num; i++ {
44         fmt.Printf("%d %v\n", i, val.Elem().Field(i).Kind())
45     }
46
47     fmt.Printf("struct has %d fields\n", num)
48
49     tag := tye.Elem().Field(0).Tag.Get("json")
50     fmt.Printf("tag=%s\n", tag)  //tag=student_name
51
52     numOfMethod := val.Elem().NumMethod()  //2
53     fmt.Printf("struct has %d methods\n", numOfMethod)
54     var params []reflect.Value
55     val.Elem().Method(0).Call(params)
56 }
57
58 func main() {
59     var a Student = Student{
60         Name:  "stu01",
61         Age:   18,
62         Score: 92.8,
63     }
64
65     result, _ := json.Marshal(a)
66     fmt.Println("json result:", string(result))
67
68     TestStruct(&a)
69     fmt.Println(a)
70 }

example

6. 反射中调用函数

Go中的函数是可以像普通的 int、float 等类型变量那样作为值的。例如:

 1 package main
 2
 3 import "fmt"
 4
 5 func Print() {
 6     fmt.Println("Hello world!")
 7 }
 8
 9 func main() {
10     f := Print
11     f()
12 }

函数作为变量

和函数作为变量类似,在反射中函数和方法的类型(Type)都是 reflect.Func,如果要调用函数的话,可以通过 Value的Call()方法。

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 func Print() {
 9     fmt.Println("Hello world!")
10 }
11
12 func main() {
13     f := Print
14     fv := reflect.ValueOf(f)
15     fmt.Println("fv is reflect.Func? ", fv.Kind() == reflect.Func)  //fv is reflect.Func?  true
16     fv.Call(nil)  //Hello world!
17 }

测试reflect.Func

Call函数的定义:func (v Value) Call(in []Value) []Value
Value 的 Call() 方法的参数是一个 Value 的 slice,对应的反射函数类型的参数,返回值也是一个 Value 的 slice,同样对应反射函数类型的返回值。

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 func Print(str string) string {
 9     str = fmt.Sprintf("hello %s", str)
10     return str
11 }
12
13 func main() {
14     fv := reflect.ValueOf(Print)
15     params := make([]reflect.Value, 1)                 // 传给Print的参数
16     params[0] = reflect.ValueOf("zhangsan")            // 参数设置为"zhangsan"
17     rs := fv.Call(params)                              // rs作为结果接受函数的返回值
18     //result: hello zhangsan
19     fmt.Println("result:", rs[0].Interface().(string)) // rs[0].Interface() ok
20 }

example

7. 反射中调用方法

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

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 type Student struct {
 9     Name string
10     Age int
11 }
12
13 func (s *Student) SetName(name string) {
14     s.Name = name
15 }
16
17 func (s *Student) SetAge(age int) {
18     s.Age = age
19 }
20
21 func (s *Student) Print() string {
22     str := fmt.Sprintf("%s is %d years old.", s.Name, s.Age)
23     return str
24 }
25
26 func main() {
27     stu := &Student {
28         Name:"zhangsan",
29         Age:20,
30     }
31
32     ref := reflect.ValueOf(stu)
33     fmt.Println("Before:", ref.MethodByName("Print").Call(nil)[0]) //Before: zhangsan is 20 years old.
34
35
36     params := make([]reflect.Value, 1)
37
38     params[0] = reflect.ValueOf("lisi")
39     ref.MethodByName("SetName").Call(params)
40     fmt.Println(stu)  //&{lisi 20}
41
42     params[0] = reflect.ValueOf(22)
43     ref.MethodByName("SetAge").Call(params)
44
45     fmt.Println(stu)  //&{lisi 22}
46     fmt.Println("After:", ref.MethodByName("Print").Call(nil)[0])  //After: lisi is 22 years old.
47 }

example

练习1:

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 type NotknownType struct {
 9     s1 string
10     s2 string
11     s3 string
12 }
13
14 func (n NotknownType) String() string {
15     return n.s1 + "-" + n.s2 + "-" + n.s3
16 }
17
18 var secret interface{} = NotknownType{"Ada", "Go", "Oberon"}
19
20 func main() {
21     value := reflect.ValueOf(secret)
22     typ := reflect.TypeOf(secret)
23     fmt.Println(typ)  // Main.NotknownType
24
25     knd := value.Kind()
26     fmt.Println(knd) // struct
27
28     for i := 0; i < value.NumField(); i++ {
29         fmt.Printf("Field %d: %v\n", i, value.Field(i))
30         //value.Field(i).SetString("C#")
31     }
32
33     results := value.Method(0).Call(nil)
34     fmt.Println(results) // [Ada - Go - Oberon]
35 }

练习

练习2:通过反射操作结构体

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 type NotknownType struct {
 9     s1 string
10     s2 string
11     s3 string
12 }
13
14 func (n NotknownType) String() string {
15     return n.s1 + "-" + n.s2 + "-" + n.s3
16 }
17
18 var secret interface{} = NotknownType{"Ada", "Go", "Oberon"}
19
20 func main() {
21     value := reflect.ValueOf(secret)
22     typ := reflect.TypeOf(secret)
23     fmt.Println(typ)  // Main.NotknownType
24
25     knd := value.Kind()
26     fmt.Println(knd) // struct
27
28     for i := 0; i < value.NumField(); i++ {
29         fmt.Printf("Field %d: %v\n", i, value.Field(i))
30         //value.Field(i).SetString("C#")
31     }
32
33     results := value.Method(0).Call(nil)
34     fmt.Println(results) // [Ada - Go - Oberon]
35 }

example

练习3:通过反射修改结构体

 1 package main
 2
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7
 8 type T struct {
 9     A int
10     B string
11 }
12
13 func main() {
14     t := T{23, "skidoo"}
15     s := reflect.ValueOf(&t).Elem()
16     typeOfT := s.Type() //main.T
17     for i := 0; i < s.NumField(); i++ {
18         f := s.Field(i)
19         fmt.Printf("%d: %s %s = %v\n", i,
20             typeOfT.Field(i).Name, f.Type(), f.Interface())
21     }
22     s.Field(0).SetInt(77)
23     s.Field(1).SetString("Sunset Strip")
24     fmt.Println("t is now", t) //t is now {77 Sunset Strip}
25 }

example

建议:反射是 Go 语言中非常强大和高级的概念,我们应该小心谨慎地使用它。使用反射编写清晰和可维护的代码是十分困难的。你应该尽可能避免使用它,只在必须用到它时,才使用反射。

图书管理系统v2版本开发:

实现一个图书管理系统v2,具有以下功能:
     a. 增加用户登录、注册功能
     b. 增加借书过期的图书界面
     c. 增加显示热门图书的功能,被借次数最多的top10
     d. 增加查看某个人的借书记录的功能

参考文献:

  • https://studygolang.com/articles/13178 (Go 系列教程 - 反射)
  • https://golang.org/pkg/reflect/
  • https://www.cnblogs.com/52php/p/6337420.html

转载于:https://www.cnblogs.com/xuejiale/p/10398405.html

Go语言学习之6 反射详解相关推荐

  1. C语言学习19:操作符详解

    目录 1.操作符种类 1.1 算术操作符 1.2 移位操作符 1.3 位操作符 1.4 赋值操作符 1.5 单目操作符 1.6 关系操作符 1.7 逻辑操作符 1.8 条件操作符 1.9 逗号表达式 ...

  2. 【C语言学习笔记】万字详解C循环

    循环语句 文章目录 循环语句 0x00 什么是语句 0x01 循环语句 1.while循环 2.do...while循环 3.for循环 3.1 for循环简介 3.2 for循环变种 4.跳出循环 ...

  3. c语言将AOE网络的数据写入TXT文档中,数据结构与算法学习辅导及习题详解.张乃孝版-C/C++文档类资源...

    数据结构与算法学习辅导及习题详解.张乃孝版.04年10月 经过几年的努力,我深深体会到,编写这种辅导书要比编写一本湝通教材困难得多. 但愿我的上述理想,在本书中能够得以体现. 本书的组织 本书继承了& ...

  4. java 7 反射_【7】java 反射详解

    [7]java 反射详解 获取Class对象的方式: 1. Class.forName("全类名"); 将字节码加载进内存,返回Class对象,多用于配置文件,将类名定义在配置文件 ...

  5. C#高级--反射详解

    C#高级–反射详解 零.文章目录 一.反射是什么 1.C#编译运行过程 高级语言->编译->dll/exe文件->CLR/JIT->机器码 2.原理解析 metadata:元数 ...

  6. java语言链栈_Java语言实现数据结构栈代码详解

    近来复习数据结构,自己动手实现了栈.栈是一种限制插入和删除只能在一个位置上的表.最基本的操作是进栈和出栈,因此,又被叫作"先进后出"表. 首先了解下栈的概念: 栈是限定仅在表头进行 ...

  7. c语言 read 文件字节没超过数组大小时会怎样_剑指信奥 | C 语言之信奥试题详解(四)...

    趣乐博思剑指信奥系列 ❝ 趣乐博思剑指信奥系列,专门针对全国青少年信息学奥林匹克联赛 NOIP 而开展的专业教育方案.开设的课程有 C 语言基础,C++ 语言基础,算法设计入门与进阶,经典试题分析与详 ...

  8. Go 语言 bytes.Buffer 源码详解之1

    转载地址:Go 语言 bytes.Buffer 源码详解之1 - lifelmy的博客 前言 前面一篇文章 Go语言 strings.Reader 源码详解,我们对 strings 包中的 Reade ...

  9. Java中大数据数组,Java基础学习笔记之数组详解

    摘要:这篇Java开发技术栏目下的"Java基础学习笔记之数组详解",介绍的技术点是"java基础学习笔记.基础学习笔记.Java基础.数组详解.学习笔记.Java&qu ...

最新文章

  1. PAT 1041. 考试座位号(15)
  2. 根据皮肤亮度来区分salmon和sea bass,这个比较好
  3. 【译】Persistent reverse (NAT bypassing) SSH tunnel access with autossh
  4. PCM复用设备功能介绍
  5. leetcode51. N 皇后(回溯算法)
  6. LeetCode 1138. 字母板上的路径
  7. 手把手教你搭建数据库服务器平台 | DBA VS 自动化运维,究竟谁与争锋?
  8. 【Spring】IOC
  9. git使用报错: fatal: Couldn‘t find remote ref master的解决方法
  10. 固定定位小技巧(HTML、CSS)
  11. 开始使用 TypeScript
  12. 机器学习--详解贝叶斯公式、朴素贝叶斯的来龙去脉(附上多方式代码实现)
  13. 人工智能与深度学习概念(3)——目标分类-CNN
  14. STM32cube配置编码器和电机
  15. media-微软Azure文字转语音
  16. whta is the前后端分离
  17. 数据库分库分表之后如何查询统计?
  18. 【前端】基于layui写的一个高级搜索(筛选)功能
  19. Python爬虫获取豆瓣电影TOP250
  20. 使用TIMESTAMPDIFF计算两个时间戳之间的时间间隔

热门文章

  1. Luogu P1879玉米田题解
  2. 数字逻辑电路与系统 课程设计:基于FPGA的乒乓球游戏
  3. Maxthon简易收藏板
  4. 可牛看图web开放平台---PHP表单上传代码分享
  5. jqgrid 列排序 php,jqGrid排序问题及解决办法
  6. 无线路由器ap(接入点)模式原理及后果
  7. 2 560.26 php,php rtrim的一个坑,很“二”的问题
  8. VS2019+WDK环境搭建
  9. 【算法】位运算符基础之某CTF赛题使用Python与易语言纯算法还原
  10. 【VCSEL】加州大学伯克利分校常瑞华教授谈VCSEL