admin 管理员组

文章数量: 1086019

Go反射学习

文章目录

  • 反射介绍:
  • 反射应用点
  • 变量-空接口-reflect.Value(Type)
  • 类型
  • 方法
  • 结构体:
  • 反射修改变量值
  • 反射操作结构体
    • Method
    • Call

反射介绍:

反射是在运行时,动态的获取变量的各种信息,如变量的类型,类别等信息

可以获得结构体的信息(字段,方法)

通过反射,可以修改变量的值,调用关联的方法

使用反射需要导入reflect包

反射弥补了静态语言上的不足

反射是实现元编程的重要手段

reflect.excalidraw

在反射中,变量,空接口,reflect.Value是可以相互转换的

反射应用点

结构体的tag

自己可以写go框架的时候,函数的适配器

变量-空接口-reflect.Value(Type)

package main  import (  "fmt"  "reflect")  func reflectTest(b interface{}) {  //得到一个reflect.Type类型  rType := reflect.TypeOf(b)  fmt.Println(rType)  //得到一个reflect.Value类型  rVal := reflect.ValueOf(b)  fmt.Println(rVal)  fmt.Printf("%T\n", rVal)  //reflect.Value类型转换成空接口  iV := rVal.Interface()  var num2 = iV.(int)  fmt.Println(num2)  }  func main() {  var num int = 100  reflectTest(num)  }

类型

一个简单的基本类型反射判断

package main  import (  "fmt"  "reflect")  func main() {  var (  a = 100  )  t := reflect.TypeOf(a)  fmt.Println(t)  }

输出
int
打印出了变量a的数据类型

对于自定义类型的类型反射判断

package main  import (  "fmt"  "reflect")  type sim int  func main() {  var a sim = 100  t := reflect.TypeOf(a)  //type是判断类型(静态类型),kind是判断基础结构(底层类型)  fmt.Println(t.Name(), t.Kind())  }

输出:
sim int

对于指针

package main  import (  "fmt"  "reflect")  func main() {  a := 10  t1, t2 := reflect.TypeOf(a), reflect.TypeOf(&a)  fmt.Println(t1, t2)  fmt.Println(t1 == t2.Elem())  
}

输出:

int *int
true

方法Elem返回指针,数据,切片,字典或通道的基类型。
所以这里的判断是true
拓展:

func main()  {fmt.Println(reflect.TypeOf(map[string]int()).Elem())fmt.Println(reflect.TypeOf([]int32{}).Elem())
}

输出:
int
int32

得到的都是他们的基类型

对于结构体

package main  import (  "fmt"  "reflect")  type student struct {  name string  age  int  
}  func main() {  var simple student  t := reflect.TypeOf(&simple)  //fmt.Println(t) 获取的结构体指针  //fmt.Println(reflect.Ptr)  //fmt.Println(t.Kind())   //判断是不是指针类型  if t.Kind() == reflect.Ptr {  t = t.Elem()  //fmt.Println(t)取得基类,是一个结构体main.student  }  //遍历结构体  for i := 0; i < t.NumField(); i++ {  f := t.Field(i)  fmt.Println(f.Name, f.Type, f.Offset)  if f.Anonymous {  for x := 0; x < f.Type.NumField(); x++ {  af := f.Type.Field(x)  fmt.Println(" ", af.Name, af.Type)  }  }  }  
}

输出:
name string 0
age int 16

取得变量的值
type获取类型,value可以获取值。

package main  import (  "fmt"  "reflect")  func main() {  a := "simple"  t := reflect.ValueOf(a)  fmt.Println(t)  
}

方法

package main  import (  "fmt"  "reflect")  type X struct{}  func (X) Test(x, y int) (int, error) {  return x + y, fmt.Errorf("err:%d", x+y)  
}  func main() {  var a X  v := reflect.ValueOf(&a)  m := v.MethodByName("Test")  in := []reflect.Value{  reflect.ValueOf(1),  reflect.ValueOf(2),  }  out := m.Call(in)  for _, v := range out {  fmt.Println(v)  }  
}

3
err:3

结构体:

package main  import (  "fmt"  "reflect")  type Student struct {  name string  age  int  
}  func reflectTest1(b interface{}) {  //得到一个reflect.Type类型  rType := reflect.TypeOf(b)  fmt.Println(rType)  //得到一个reflect.Value类型  rVal := reflect.ValueOf(b)  fmt.Println(rVal)  fmt.Printf("%T\n", rVal)  // reflect.Value类型变成空接口  iV := rVal.Interface()  fmt.Printf("%v,%T \n", iV, iV)  // 通过断言转换成需要的类型  stu, ok := iV.(Student)  if ok {  fmt.Println("stu.name:", stu.name)  }  
}  func main() {  stu := Student{  "simple",  10,  }  reflectTest1(stu)  
}

反射修改变量值

package main  import (  "fmt"  "reflect")  func reflect1(b interface{}) {  rVal := reflect.ValueOf(b)  //fmt.Println(rVal.Kind()) 类别是一个指针  rVal1 := rVal.Elem()//取指针指向的值  rVal1.SetInt(20)  
}  func main() {  num := 100  reflect1(&num)  fmt.Println(num)  
}

输出
20

package main  import (  "fmt"  "reflect")  func main() {  name := "simple"  rVal := reflect.ValueOf(&name)  rVal.Elem().SetString("jack")  fmt.Println(name)  
}

输出:
jack

反射操作结构体

Method

func (Value) [Method]func (v [Value]) Method(i int) Value

返回v持有值类型的第i个方法的已绑定(到v的持有值的)状态的函数形式的Value封装。返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。如果i出界,或者v的持有值是接口类型的零值(nil),会panic。

Call

func (Value) Callfunc (v Value) Call(in [][Value]) [][Value]

Call方法使用输入的参数in调用v持有的函数。例如,如果len(in) == 3,v.Call(in)代表调用v(in[0], in[1], in[2])(其中Value值表示其持有值)。如果v的Kind不是Func会panic。它返回函数所有输出结果的Value封装的切片。和go代码一样,每一个输入实参的持有值都必须可以直接赋值给函数对应输入参数的类型。如果v持有值是可变参数函数,Call方法会自行创建一个代表可变参数的切片,将对应可变参数的值都拷贝到里面。

package main  import (  "fmt"  "reflect")  // 定义结构体  
type Monster struct {  Name  string `json:"name"`  age   int    `json:"age"`  Score float32  sex   string  
}  // Print方法  
func (s Monster) Print() {  fmt.Println("start")  fmt.Println(s)  fmt.Println("end")  }  // 和方法  
func (s Monster) GetSum(n1, n2 int) int {  return n1 + n2  
}  // Set方法  
func (s Monster) Set(name string, age int, score float32, sex string) {  s.Name = name  s.age = age  s.Score = score  s.sex = sex  
}  func testStruct(a interface{}) {  //获取reflect.Type和reflect.Value  typ := reflect.TypeOf(a)  val := reflect.ValueOf(a)  //看看类别  kd := val.Kind()  //看看是不是结构体  if kd != reflect.Struct {  fmt.Println("不是结构体")  return  }  //获取字段数量  num := val.NumField()  fmt.Printf("struct has %d fields\n", num)  //遍历字段  for i := 0; i < num; i++ {  fmt.Printf("Field %d : 值为:%v\n", i, val.Field(i))  //获取tag  tagVal := typ.Field(i).Tag.Get("json")  //判断是否有tag,有就输出  if tagVal != "" {  fmt.Printf("Field %d: tag为:%v\n", i, tagVal)  }  }  //获取方法数量  numofMethod := val.NumMethod()  fmt.Printf("结构体有%d个方法\n", numofMethod)  //调用第二个函数,第二个函数没有参数  val.Method(1).Call(nil)  //反射的函数排序是按照函数名的ascii码来排序的,G,P,S来排序的,所以这里的第二个就是Print  //准备函数的参数,参数要是[]reflect.Value切片类型的  var params []reflect.Value  params = append(params, reflect.ValueOf(10))  params = append(params, reflect.ValueOf(40))  //一个10,一个是40  //调用函数,并传入参数  res := val.Method(0).Call(params)  //res还是一个切片,返回的是一个切片  fmt.Println(res)  fmt.Println("res= ", res[0].Int())  }  func main() {  //定义一个实例  a := Monster{  "simple",  20,  30.1,  "难",  }  //进入反射  testStruct(a)  }

输出:

struct has 4 fields
Field 0 : 值为:simple
Field 0: tag为:name  
Field 1 : 值为:20    
Field 1: tag为:age   
Field 2 : 值为:30.1  
Field 3 : 值为:难    
结构体有3个方法       
start                 
{simple 20 30.1 难}
end
50
res=  50

本文标签: Go反射学习