一篇带给你Go语言的反射机制

开发 后端
在计算机学中,反射式编程(英语:reflective programming)或反射(英语:reflection),是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

因为没有强类型语言的经验,反射这个概念,之前确实没怎么接触过。在维基百科上搜了一下,具体解释如下:

  • 在计算机学中,反射式编程(英语:reflective programming)或反射(英语:reflection),是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

go 中的反射也是这种作用,可以在程序运行期间,获取变量的类型与值的信息,然后进行访问或或者修改。go 语言中,内置了 reflect 包,用来获取一个变量的类型(type)与值(value),对应的方法分别为 reflect.TypeOf() 和 reflect.ValueOf()。

反射类型

TypeOf 方法,会返回该变量的类型对象,类型对象下可以获取到变量的类型与种类。

  1. import ( 
  2.  "fmt" 
  3.  "reflect" 
  4.  
  5. func main() { 
  6.  // 定义一个int类型的变量 
  7.  var i int = 1 
  8.  // 获取变量的类型对象 
  9.  var typeOfNum = reflect.TypeOf(i)  
  10.  
  11.   // 输出类型与种类 
  12.   typeOfNumName = typeOfNum.Name() 
  13.   typeOfNumKind = typeOfNum.Kind() 
  14.   fmt.Printf("name: %s, kind: %s", typeOfNumName, typeOfNumKind) 

可以看到,此时的类型与种类都为 int。

类型与种类

类型表示定义变量的时候指定的类型,可以反映 type 关键字定义的类型,而种类是变量最终归属的类型。说起来可能比较苍白,我们直接上代码。

  1. type num int 
  2.  
  3. // 定义一个num类型的变量 
  4. var i num = 1 
  5. var typeOfNum = reflect.TypeOf(i)  

可以看到,此时的类型为 num,种类为 int。

对于一些引用类型的变量,比如切片、函数、结构体,kind 都能准确反映其底层的类型。

  1. func printTypeOf(typeOf reflect.Type) { 
  2.  fmt.Printf("name: %s, kind: %s\n", typeOf.Name(), typeOf.Kind()) 
  3.  
  4. type Person struct {} 
  5. type IntSlice []int 
  6. func main() { 
  7.  var a = IntSlice{} 
  8.  var b = Person{} 
  9.  printTypeOf(reflect.TypeOf(a)) 
  10.  printTypeOf(reflect.TypeOf(b)) 

 

而面对匿名结构体或者匿名函数,其类型值会返回为空。

  1. func main() { 
  2.  var a = struct {}{} 
  3.  printTypeOf(reflect.TypeOf(a)) 

 

反射值

ValueOf 方法,可以获取一个变量的值。

  1. var i = 3.1415926 
  2. var s = "欢迎关注我的公众号:『自然醒的笔记本』" 
  3.  
  4. fmt.Println(reflect.ValueOf(s)) 
  5. fmt.Println(reflect.ValueOf(i)) 

 

通过反射的值对象,也能取到变量的种类,并且还能根据其种类,调用对应的方法获取变量的真实值。

  1. var i = 100 
  2. var v = reflect.ValueOf(i) 
  3.  
  4. fmt.Println(v.Int()) // 如果值是 Int 类型,可以通过 Int 方法获取具体值 
  5. fmt.Println(v.Kind()) 

 

修改值

通过反射得到的值对象,可以对变量本身的值进行修改。首先,在获取反射值时,不能直接获取变量的反射值,而是要先取其指针的值对象。

  1. var i = 100 
  2. var v = reflect.ValueOf(&i) // 取出变量i的指针的值对象 
  3.  
  4. fmt.Println(v.Kind(), v) 

取出指针的值对象之后,不能立即赋值,因为此时拿到的是变量的地址。

要赋值的话,需要先调用 Elem 方法,取出具体元素,然后进行赋值。

  1. var i = 100 
  2. var v = reflect.ValueOf(&i) // 取出变量i的指针的值对象 
  3.  
  4. var e = v.Elem() 
  5. e.SetInt(500) // 修改元素值 
  6.  
  7. fmt.Println(e.Kind(), i) 

 

值对象与结构体

前面介绍过,通过反射可以得到变量的值,对于结构体来说,也是一样。

  1. type Person struct { 
  2.  name string 
  3.  age int 
  4.  gender string 
  5.  address string 
  6.  
  7. var p = Person{"Shenfq", 25, "男""湖南长沙"
  8. var v = reflect.ValueOf(p) 
  9.  
  10. fmt.Println(v.Kind(), v) 

 

反射值对象还提供了一些方法,专门用来针对结构体成员的信息获取。

NumField()

NumField() 可以获取结构体成员的具体数量。

  1. var p = Person{"Shenfq", 25, "男""湖南长沙"
  2. var v = reflect.ValueOf(p) 
  3.  
  4. fmt.Println("Person 结构体成员数:", v.NumField()) 

 

Field()

Field() 可以获取结构体指定索引位置的成员的反射值。

  1. var p = Person{"Shenfq", 25, "男""湖南长沙"
  2. var v = reflect.ValueOf(p) 
  3. var num = v.NumField() 
  4. for i :=0; i < num; i++ { 
  5.   var val = v.Field(i) 
  6.   fmt.Printf("Person[%d]: %s %v\n", i, val.Type(), val) 

 

FieldByName()

FieldByName() 可以获取结构体指定成员名称的成员的反射值。

  1. var p = Person{"Shenfq", 25, "男""湖南长沙"
  2. var v = reflect.ValueOf(p) 
  3. var vOfName = v.FieldByName("name"
  4. fmt.Printf("Person[name]: %s %v\n", vOfName.Type(), vOfName) 

 

- END -

责任编辑:姜华 来源: 自然醒的笔记本
相关推荐

2021-06-24 06:35:00

Go语言进程

2021-04-09 10:38:59

Go 语言数组与切片

2021-04-06 10:19:36

Go语言基础技术

2021-04-01 10:51:55

MySQL锁机制数据库

2021-04-20 06:12:09

Swift 反射 Mirror反射机制

2021-03-24 06:06:13

Go并发编程Singlefligh

2021-07-12 06:11:14

SkyWalking 仪表板UI篇

2021-10-28 08:51:53

GPIO软件框架 Linux

2023-03-13 09:31:04

2021-04-23 08:59:35

ClickHouse集群搭建数据库

2021-07-08 07:30:13

Webpack 前端Tree shakin

2021-04-14 07:55:45

Swift 协议Protocol

2022-02-25 15:50:05

OpenHarmonToggle组件鸿蒙

2021-05-08 08:36:40

ObjectString前端

2023-03-29 07:45:58

VS编辑区编程工具

2021-01-28 08:55:48

Elasticsear数据库数据存储

2021-04-08 11:00:56

CountDownLaJava进阶开发

2021-04-14 14:16:58

HttpHttp协议网络协议

2021-03-12 09:21:31

MySQL数据库逻辑架构

2021-07-21 09:48:20

etcd-wal模块解析数据库
点赞
收藏

51CTO技术栈公众号