golang的反射--reflact标准库分析

目录

一:反射介绍

二:反射的type(常见于结构体获取结构体tag)

获取结构体tag示例

三:反射的value(常见于调用函数)

调用函数示例:

反射调用结构体方法示例


一:反射介绍

反射包中的所有方法基本都是围绕着 Type 和 Value 这两个类型设计的。 我们通过
reflect.TypeOf 、 reflect.ValueOf 可以将一个普通的变量转换成『反射』 包中提供的 Type 和 Value , 随后就可以使用反射包中的方法对它们进行复杂的操作。

 

二:反射的type(常见于结构体获取结构体tag)

通过反射得到结构体类型后,也可以针对结构体的某个字段进行type和value的反射。

结构体的type

结构体的type都是StructField结构;通过Field,FieldByIndex方法得到结构体某个成员的类型:StructField

StructField 的结构如下:

type StructField struct {
    Name string          // 字段名
    PkgPath string       // 字段路径
    Type      Type       // 字段反射类型对象
    Tag       StructTag  // 字段的结构体标签
    Offset    uintptr    // 字段在结构体中的相对偏移
    Index     []int      // Type.FieldByIndex中的返回的索引值
    Anonymous bool       // 是否为匿名字段
}

获取结构体type的方法:

Field(i int) StructField    // 根据索引返回索引对应的结构体字段的信息,当值不是结构体或索引超界时发生宕机

// 返回结构体成员字段数量,当类型不是结构体或索引超界时发生宕机
NumField() int

//根据给定字符串返回字符串对应的结构体字段的信息,没有找到时 bool 返回 false,当类型不是结构体或索引超界时发生宕机
FieldByName(name string) (StructField, bool)

//用于结构体嵌套访问,根据 []int 提供的每个结构体的字段索引,返回字段的信息,没有找到时返回零值。当类型不是结构体或索引超界时发生宕机
FieldByIndex(index []int) StructField

//根据匹配函数匹配需要的字段,当值不是结构体或索引超界时发生宕机
FieldByNameFunc(match func(string) bool) (StructField,bool)	

通过StructField 中 Tag structTag字段,又可以通过key获取对应的tag:

StructTag 拥有一些方法,可以进行 Tag 信息的解析和提取:

// 根据 Tag 中的键获取对应的值,例如`key1:"value1" key2:"value2"`的 Tag 中,可以传入“key1”获得“value1”。
func (tag StructTag) Get(key string) string


// 根据 Tag 中的键,查询值是否存在。
func (tag StructTag) Lookup(key string) (value string, ok bool)

获取结构体tag示例

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type cat struct {
        Name string
        Type int `json:"type" id:"100"`
    }
    // 通过reflect.TypeOf获取反射的type类型
    typeOfCat := reflect.TypeOf(cat{})

    // 通过type类型的FieldByName方法,获取字段名称为Tpye的结构体字段,其类型是StructField
    if catType, ok := typeOfCat.FieldByName("Type"); ok {

        // 结构体字段中的成员Tag通过Get方法,获取key为json的值,
        // 也就是这里获取到了 Type int `json:"type" id:"100"` 中的"type",如果是根据id来获取,那么就是获取到100
        fmt.Println(catType.Tag.Get("json"))
    }
}

 

三:反射的value(常见于调用函数)

如果反射值对象(reflect.Value)中值的类型为函数时,可以通过 reflect.Value 调用该函数。

使用反射调用函数时,需要将参数使用反射值对象的切片 []reflect.Value 构造后传入 Call() 方法中,调用完成时,函数的返回值通过 []reflect.Value 返回。

调用函数示例:

package main

import (
    "fmt"
    "reflect"
)

// 普通函数
func add(a, b int) int {

    return a + b
}

func main() {

    // 将函数包装为反射值对象
    funcValue := reflect.ValueOf(add)

    // 构造函数参数, 传入两个整型值
    paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}

    // 反射调用函数
    retList := funcValue.Call(paramList)

    // 获取第一个返回值, 取整数值
    fmt.Println(retList[0].Int())
}

反射调用结构体方法示例

调用结构体方法也是一样的

package main
import (
    "fmt"
    "reflect"
)
type User struct{
    Id int 
    Name string
    Age int
}
//设置Name方法
func (u *User) SetName(name string) string{
  oldName := u.Name
  u.Name = name
  return oldName 
}
//年龄数+1
func (u *User) AddAge() bool {
    u.Age++
    return true
}
//测试方法
func (u User) TestUser() {
  fmt.Println("我只是输出某些内容而已....")
}

func main(){
    //通过反射的方式调用结构体类型的方法
    var setNameStr string = "SetName"
    var addAgeStr string = "AddAge"
    user := User{
        Id : 1,
        Name : "env107" , 
        Age : 18 ,
    }
    //1.获取到结构体类型变量的反射类型
    refUser:= reflect.ValueOf(&user)  //需要传入指针,后面再解析
    fmt.Println(refUser)
    //2.获取确切的方法名
    //带参数调用方式
    //重点在这,通过MethodByName,通过字符串获取对应的结构体的方法,返回值是一个函数,然后调用过程和上面的例子的函数调用过程是一样的。
    setNameMethod := refUser.MethodByName( setNameStr  )    
	
    args := []reflect.Value{ reflect.ValueOf("Mike")  } //构造一个类型为reflect.Value的切片
    setNameMethod.Call(args) //返回Value类型
    //不带参数调用方式
    addAgeMethod := refUser.MethodByName( addAgeStr )
    addAgeMethod.Call( make([]reflect.Value , 0) )
    
    fmt.Println("User.Name = ",user.Name)
    fmt.Println("User.Age = ",user.Age)

}

 

你可能感兴趣的:(MyGolang,golang,reflect)