golang 反射总结

319 阅读3分钟

前言

golang 反射

指的是程序在运行中可以动态获取变量的类型,方法,属性,并且可以调用方法以及修改属性。 golang 反射核心是通过 reflect.TypeOf() 和 reflect.ValueOf() 展开,下边我开始为大家展开golang 反射的谜团。

正文

1 获取变量类型

1.1 通过 reflect.TypeOf() 获取

package main

import (
   "fmt"
   "reflect"
)

func main() {
   i := 1
   str := "hello world"
   obj := struct {
   }{}
   m := map[string]interface{}{}
   var sl []interface{}
   fmt.Println(reflect.TypeOf(i))
   fmt.Println(reflect.TypeOf(str))
   fmt.Println(reflect.TypeOf(obj))
   fmt.Println(reflect.TypeOf(m))
   fmt.Println(reflect.TypeOf(sl))
}

打印结果

int
string
struct {}
map[string]interface {}
[]interface {}

1.2 通过 reflect.ValueOf() 获取

package main

import (
   "fmt"
   "reflect"
)

func main() {
   i := 1
   str := "hello world"
   obj := struct {
   }{}
   m := map[string]interface{}{}
   var sl []interface{}
   fmt.Println(reflect.ValueOf(i).Type())
   fmt.Println(reflect.ValueOf(str).Type())
   fmt.Println(reflect.ValueOf(obj).Type())
   fmt.Println(reflect.ValueOf(m).Type())
   fmt.Println(reflect.ValueOf(sl).Type())
}

打印结果 和 reflect.TypeOf() 是一样的

int
string
struct {}
map[string]interface {}
[]interface {}

1.3 type 和 kind

对于基本类型 type 和 kind 是一样的,对于自定义 类型,kind 和 type 就不一样的。

package main

import (
   "fmt"
   "reflect"
)

type integer int

func main() {
   var a integer
   var b int64
   fmt.Println(reflect.TypeOf(a), reflect.TypeOf(a).Kind())
   fmt.Println(reflect.TypeOf(b), reflect.TypeOf(b).Kind())
}

打印结果

main.integer int
int64 int64

说明type是变量的上层类型,对自定义类型integer ,他的type是main.integer,但是底层类型是int,然而基本类型kind 和 type 是一样的。

2 获取自定变量属性

2.1 获取struct类型其属性值

package main

import (
   "fmt"
   "reflect"
)

type people struct {
   age int64
   sex string
}

func main() {
   person := people{18, "女"}
   c := reflect.ValueOf(person)
   fmt.Println(c.NumField())             //获取属性数量
   fmt.Println(c.Field(0))               //获取一个属性值
   fmt.Println(c.FieldByIndex([]int{1})) //获取第二个属性值
   fmt.Println(c.FieldByName("sex"))     //获取属性名称为sex的属性值
}

2.2 获取struct类型属性名称和类型

package main

import (
   "fmt"
   "reflect"
)

type people struct {
   age int64
   sex string
}

func main() {
   person := people{18, "女"}
   c := reflect.ValueOf(person)
   fmt.Println(c.Type().Field(0).Type) //获取第一个属性类型
   fmt.Println(c.Type().Field(0).Name) //获取第一个属性名称
   fmt.Println(c.Type().Field(1).Type) //获取第二个属性类型
   fmt.Println(c.Type().Field(1).Name) //获取第二个属性名称
}

打印结果

int64
age
string
sex

3 修改变量属性值

3.1 修改struct变量属性值

package main

import (
   "fmt"
   "reflect"
)

type people struct {
   age int64
   Sex string
}

func main() {
   person := people{18, "女"}
   c := reflect.ValueOf(&person).Elem() //传递变量地址,且获取变量值
   if c.FieldByName("Sex").CanSet() {   //判断变量是否可写
      c.FieldByName("Sex").SetString("男") //修改sex值
   }
   fmt.Println(c)
}

打印结果

{18 男}

如果要struct 变量值,那么 valueOf() 传递的是变量的地址,然后通过Elem获取变量对于的值,然后才能修改,如果仅仅传递量值,那么变量是不能被修改的

3.2 修改其他类型变量

package main

import (
   "fmt"
   "reflect"
)

type people struct {
   age int64
   Sex string
}

func main() {
   a := 1 //创建变量1
   reflect.ValueOf(&a).Elem().SetInt(2)
   fmt.Println(a)
}

打印结果

2

4 调用变量的方法

4.1 获取struct变量的方法

package main

import (
   "fmt"
   "reflect"
)

type people struct {
   age int64
   Sex string
}

func (p *people) Say(str string) string {
   fmt.Println(str)
   return str
}

func main() {
   p := people{18, "男"}
   v := reflect.ValueOf(&p)
   fmt.Println(v.NumMethod()) //获取方法数量
   method := v.MethodByName("Say")
   fmt.Println(method.Type().NumIn()) //获取参数个数
}

打印结果

1
1

4.2 通过valueOf 调用 stuct的方法

package main

import (
   "fmt"
   "reflect"
)

type people struct {
   age int64
   Sex string
}

func (p *people) Say(str string) string {
   fmt.Println(str)
   return str
}

func main() {
   p := people{18, "男"} //初始化变量
   v := reflect.ValueOf(&p) //获取变量的值
   method := v.MethodByName("Say") //获取变量的方法
   args := make([]reflect.Value,method.Type().NumIn()) //构成方法参数
   args[0] = reflect.ValueOf("hello world") //第一个参数赋值
   result := method.Call(args) //调用方法
   fmt.Println(result[0])//打印结果
}

输出

hello world
hello world

4.3 通过typeOf 调用 stuct的方法

package main

import (
   "fmt"
   "reflect"
)

type people struct {
   age int64
   Sex string
}

func (p *people) Say(str string) string {
   fmt.Println(str)
   return str
}

func main() {
   p := people{18, "男"} //初始化变量
   t := reflect.TypeOf(&p) //获取变量的类型
   method, _ := t.MethodByName("Say") // 获取变量的方法
   args := make([]reflect.Value,method.Type.NumIn()) // 构成参数,于valueof 不同,typeof 需要传对象本身进去
   args[0] = reflect.ValueOf(&p) // 第一个参数为变量本身
   args[1] = reflect.ValueOf("hello world") //第二个才为参数
   result := method.Func.Call(args) //调用方法
   fmt.Println(result[0])//打印结果
}

打印结果

hello world
hello world

总结

golang 反射可以支持在程序中根据变量获取边的属性方法,已经修改属性,调用变量的方法,可以大幅度降低我们的代码量。