我命由我,不由天!


  • 搜索
prometheus docker golang linux kubernetes

go-reflect

发表于 2021-01-04 | 分类于 golang | 0 | 阅读次数 249

Name and Kind

typeOfCat := reflect.TypeOf(cat{})
typeOfCat.Name() // cat
typeOfCat.Kind()  // struct
typeOfCat.NumField() // 2 几个字段
typeOfCat.Field(i) //第几个字段
typeOfCat.FieldByName("Type") // 找名为Type的字段
typeOfCat.MethodByName("Start") // 找名为Start的方法
if typeofCat.Kind() == "ptr"{
  res := typeOfCat.Elem()  // 等价于*ptr
  res.Name()
  res.Kind()
}

reflect.ValueOf()和reflect.Value

valueOfA := reflect.ValueOf(a)
var getA int = valueOfA.Interface().(int)
var getA2 int = int(valueOfA.Int())

通过反射获取值信息

使用反射值对象包装任意值

Go语言中,使用 reflect.ValueOf() 函数获得值的反射值对象(reflect.Value)。书写格式如下:

value := reflect.ValueOf(rawValue)

reflect.ValueOf 返回 reflect.Value 类型,包含有 rawValue 的值信息。reflect.Value 与原值间可以通过值包装和值获取互相转化。reflect.Value 是一些反射操作的重要类型,如反射调用函数。

从反射值对象获取被包装的值

Go语言中可以通过 reflect.Value 重新获得原始值。

1) 从反射值对象(reflect.Value)中获取值的方法

可以通过下面几种方法从反射值对象 reflect.Value 中获取原值,如下表所示。

方法名说 明
Interface() interface {}将值以 interface{} 类型返回,可以通过类型断言转换为指定类型
Int() int64将值以 int 类型返回,所有有符号整型均可以此方式返回
Uint() uint64将值以 uint 类型返回,所有无符号整型均可以此方式返回
Float() float64将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回
Bool() bool将值以 bool 类型返回
Bytes() []bytes将值以字节数组 []bytes 类型返回
String() string将值以字符串类型返回
2) 从反射值对象(reflect.Value)中获取值的例子

下面代码中,将整型变量中的值使用 reflect.Value 获取反射值对象(reflect.Value)。再通过 reflect.Value 的 Interface() 方法获得 interface{} 类型的原值,通过 int 类型对应的 reflect.Value 的 Int() 方法获得整型值。

package main
import (
    "fmt"
    "reflect"
)
func main() {
    // 声明整型变量a并赋初值
    var a int = 1024
    // 获取变量a的反射值对象
    valueOfA := reflect.ValueOf(a)
    // 获取interface{}类型的值, 通过类型断言转换
    var getA int = valueOfA.Interface().(int)
    // 获取64位的值, 强制类型转换为int类型
    var getA2 int = int(valueOfA.Int())
    fmt.Println(getA, getA2)
}

代码输出如下:

1024 1024

通过reflect反射修改值

判断反射值是否可以修改?CanSet(),是否能被取址CanAddr()

结构体成员中,如果字段没有被导出,即便不使用反射也可以被访问,但不能通过反射修改

  1. 指针指向的具体元素

    x := 1
    v := reflect.ValueOf(&x)
    v = v.Elem()
    
  2. slice的元素

    s := []int{1,1}
    v := reflect.ValueOf(s)
    e := v.Index(0)
    
  3. 可寻址的结构体的字段(指向结构体的指针)

    type Orange struct{
      Size int
    }
    a := Orange{99}
    v := reflect.ValueOf(&a)
    v = v.Elem()
    field = v.FieldByName("field")
    
  4. 可寻址的数组的元素(指向数组的指针)

    a := [2]int{1,1}
    v := reflect.ValueOf(&a)
    v = v.Elem()
    vIndex = v.Index(0)
    vIndex.SetInt(1)
    vIndex.SetInt(1)
    
Set(x Value)将值设置为传入的反射值对象的值
Setlnt(x int64)使用 int64 设置值。当值的类型不是 int、int8、int16、 int32、int64 时会发生宕机
SetUint(x uint64)使用 uint64 设置值。当值的类型不是 uint、uint8、uint16、uint32、uint64 时会发生宕机
SetFloat(x float64)使用 float64 设置值。当值的类型不是 float32、float64 时会发生宕机
SetBool(x bool)使用 bool 设置值。当值的类型不是 bod 时会发生宕机
SetBytes(x []byte)设置字节数组 []bytes值。当值的类型不是 []byte 时会发生宕机
SetString(x string)设置字符串值。当值的类型不是 string 时会发生宕机

通过类型信息创建实例

var a int
typeOfA := reflect.TypeOf(a)
aIns := reflect.New(typeOfA) // 等价于 new(int)
fmt.Println(aIns.Type(), aIns.Kind())

Go语言结构体标签(Struct Tag)

结构体标签的格式

tag 格式:json:"type" id:"100"

注意:

  1. key:“value” 冒号后没有空格
  2. 两个key之间,一个空格,多一个都不行
type cat struct{
    Name string
    Type int `json:"type" id:"100"`
}

从结构体标签中获取值

typeOfCat := reflect.TypeOf(cat{})
catType = typeOfCat.FieldByName("Type")
catType.Tag.Get("json")

常见用法

动态调用函数(无参数)

  1. 直接reflect.Valueof
  2. MethodByName
  3. 调用Call方法
type T struct {}

func main() {
    name := "Do"
    t := &T{}
    reflect.ValueOf(t).MethodByName(name).Call(nil)
}

func (t *T) Do() {
    fmt.Println("hello")
}

动态调用函数(有参数)

  1. 创建reflect.Value切片
  2. reflect.Valueof(t).MethodByName(string) 调用Call方法传入参数
type T struct{}

func main() {
    name := "Do"
    t := &T{}
    a := reflect.ValueOf(1111)
    b := reflect.ValueOf("world")
    in := []reflect.Value{a, b}
    reflect.ValueOf(t).MethodByName(name).Call(in)
}

func (t *T) Do(a int, b string) {
    fmt.Println("hello" + b, a)
}

接收返回值

  1. 返回[]reflect.Value
  2. 调用Interface()方法,转为interface{}类型
  3. 断言类型
type T struct{}

func main() {
    name := "Do"
    t := &T{}
    ret := reflect.ValueOf(t).MethodByName(name).Call(nil)
    fmt.Printf("strValue: %[1]v\nerrValue: %[2]v\nstrType: %[1]T\nerrType: %[2]T", ret[0], ret[1].Interface().(error))
}

func (t *T) Do() (string, error) {
    return "hello", errors.New("new error")
}

Tag解析

  1. reflect.Typeof有NumField,Field根据序号索取,取其中的tag
type T struct{
	Name string `json:"name"`
	Age int `json:"age"`
}

func main() {
	a := T{}
	tt := reflect.TypeOf(a)
	for i:=0;i<tt.NumField();i++{
		fmt.Println(tt.Field(i).Name)
		fmt.Println(tt.Field(i).Tag.Get("json"))
		fmt.Println(tt.Field(i).Tag.Lookup("json"))
		fmt.Println(tt.Field(i).Type)
	}
}

通过kind()处理不同分支

  1. reflect.Typeof().Kind()
func main() {
	t := 1
	a := reflect.TypeOf(t)
	switch a.Kind() {
	case reflect.String:
		fmt.Println("string")
	case reflect.Int:
		fmt.Println("int")
	default:
		fmt.Println("default")
	}
}

判断结构体是否实现接口

  1. 将nil强转为接口指针类型,通过reflect.Typeof().Elem获取其接口类型
  2. reflect.Typeof调用方法Implements来判断
type IT interface {
	test1()
}

type T struct {
	A string
}

func (t *T) test1() {}

func main() {
	t := &T{}
	elem := reflect.TypeOf((*IT)(nil)).Elem()
	if reflect.TypeOf(t).Implements(elem) {
		fmt.Println("OK")
	}
}

直接通过接口强转判断

  1. 将类型转换为 reflect.Value
  2. 将reflect.Value调用Interface()方法转为接口类型
  3. 类型断言
type ITester interface {
	test1()
}

type User struct {
	A string
}


func main() {
	u := &User{}
	v := reflect.ValueOf(u)
	val, ok := v.Interface().(ITester)
	if ok {
		fmt.Println("Support Interface", val)
	}
}
  • 本文作者: Dante
  • 本文链接: https://gaodongfei.com/archives/go-reflect
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# golang
Golang的interface
goroutine and channel
  • 文章目录
  • 站点概览
Dante

Dante

119 日志
5 分类
5 标签
RSS
Creative Commons
0%
© 2023 Dante
由 Halo 强力驱动
|
主题 - NexT.Pisces v5.1.4
沪ICP备2020033702号