Go reflect 反射

发布时间: 更新时间: 总字数:2016 阅读时间:5m 作者: IP属地: 分享 复制网址

反射是指在运行时动态的访问和修改任意类型对象的结构和成员,在golang中提供reflect包提供反射功能,每一个变量都有两个属性:类型(Type)和值(Value)

介绍

反射的作用:

  • 获取变量类型信息
  • 获取变量值信息
  • 动态更新变量的值
  • 动态调用函数
  • 动态创建结构体
  • 动态创建函数并表用

Type

reflect.Type 是一个接口类型,用例获取变量类型的信息,可以通过 reflect.TypeOf 函数获取某个变量的类型信息

方法:

  • 通用方法
    • Name():类型名
    • PkgPath():包路径
    • Kind():类型枚举值
    • String():字符串
    • Comparable():是否可比较
    • Implements(Type):是否实现某种接口
    • AssignableTo(Type):是否可以赋值给某类型
    • ConvertibleTo(Type):是否可以转化为某类型
    • NumMethod():方法个数
    • Method(int):通过索引获取方法类型,Method 结构体常用属性
      • Name 方法名
      • Type 函数类型
      • Func 方法值(Value)
    • MethodByName(string):通过方法名获取方法
  • 特定方法
    • 数字类型:reflect.Int*/reflect.Uint*/reflect.Float*/reflect.Complex*
      • Bits 获取占用字节位数
    • reflect.Array
      • Len() 获取长度
      • Elem() 获取数组元素类型
    • reflect.Slice
      • Elem() 获取切片元素类型
    • reflect.Map
      • Key() 获取映射键类型
      • Elem() 获取映射值类型
    • reflect.Ptr
      • Elem() 获取指针指向的值类型
    • reflect.Func
      • IsVariadic() 是否具有可变参数
      • NumIn() 参数个数
      • In(int) 通过索引获取参数类型
      • NumOut 返回值个数
      • out(int) 通过索引获取返回值类型
    • reflect.Struct
      • NumField 属性个数
      • Field(int) 通过索引获取属性
        • StructField 结构体属性
        • Name 属性名
        • Anonymous 是否为匿名
        • Tag 标签,其中 StructTag 常用方法
          • Get(string)
          • Lookup(string)
      • FieldByName(string) 通过属性名获取属性

Value

reflect.Value 是一个结构体类型,用户获取变量值的信息,可以通过 reflect.ValueOf 函数获取某个变量值的信息

方法:

  • 通用方法
    • Type() 获取值类型
    • CanAddr() 是否可以获取地址
    • Addr() 获取地址
    • CanInterface() 是否可以获取接口
    • InterfaceData()
    • Interface() 将变量转化为 interface{}
    • CanSet() 是否可更新
    • isValid() 是否初始化为零值
    • Kind() 获取值类型枚举值
    • NumMethod() 方法个数
    • Method(int) 通过索引获取方法值
    • MethodByName(string) 通过方法名称获取方法值
    • Convert(Type) 转换为对应类型的值
  • 修改方法
    • Set/Set* 设置变量值
  • 调用方法
    • Call
    • CallSlice
  • 特定类型方法
    • reflect.Int*/reflect.Uint*
      • Int() 获取 Int 对应类型值
      • Uint() 获取 Uint 对应类型值
    • reflect.Float*
      • Float() 获取 Float 对应类型值
    • reflect.Complex*
      • Complex() 获取 Complex 对应类型值
    • reflect.Array
      • Len() 获取数组长度
      • Index(int) 根据索引获取数组元素
      • Slice(int, int) 获取切片
      • Slice3(int, int, int) 获取切片
    • reflect.Slice
      • IsNil() 判断是否为 nil
      • Len() 获取元素数量
      • Cap() 获取切片容量
      • Index(int) 根据索引获取元素
      • Slice(int, int) 获取切片
      • Slice3(int, int, int) 获取切片
    • reflect.Map
      • IsNil() 判断是否为 nil
      • Len() 获取元素数量
      • MapKeys() 获取所有键
      • MapIndex(Value) 根据键获取值
      • MapRange() 获取键值组成的可迭代对象
    • reflect.Ptr
      • Elem() 获取指向值类型(解引用)
    • reflect.Func
      • IsVariadic() 是否具有可变参数
      • NumIn() 参数个数
      • In(int) 通过索引获取参数类型
      • NumOut 返回值个数
      • out(int) 通过索引获取返回值类型
    • reflect.Struct
      • NumField 属性个数
      • Field(int) 通过索引获取属性
        • StructField 结构体属性
        • Name 属性名
        • Anonymous 是否为匿名
        • Tag 标签,其中 StructTag 常用方法
          • Get(string)
          • Lookup(string)
      • FieldByName(string) 通过属性名获取属性

示例

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var i int = 6
	//var i string = "hello"
	//var i []string = []string{"helko", "world"}
	fmt.Printf("%T\n", i) // int

	// 获取变量类型
	var t reflect.Type = reflect.TypeOf(i)
	fmt.Println(t)                                           // int
	fmt.Println(t.String())                                  // int
	fmt.Println(t.Name())                                    // int
	fmt.Printf("%d %v\n", t.Kind(), t.Kind() == reflect.Int) // 2 true // https://pkg.go.dev/reflect#Kind
	fmt.Println(t.Size())                                    // 8
	fmt.Println(t.Comparable())                              // true

	var v reflect.Value = reflect.ValueOf(i)
	fmt.Println(v) // 6
	//fmt.Println(v.NumField())
}

反射的应用

反射常用在动态处理所有数据类型(json、xml序列化和反序列化)或调动所有函数(路由函数调用)的场景

encoding/json

在对内存数据进行持久化存储或网络交互时,常采用json格式化字符串,golang提供 encoding/json 包对齐进行序列化和反序列化 针对结构体也可以通过声明属性与json字符串的转换关系,

json 格式示例:

// 数组
[1, "2", true, 3.14, {}]

// 映射
{
  "key1": 1,
  "key2": "2",
  "key3": true,
  "key4": 3.14,
  "key5": {
    "key5-1": 0
  }
}

encoding/json 示例

  • json.Marshal 序列化,将内存数据抓华为字符串、字节切片
  • json.Unmarshal 反序列化,将字符串、字节切片转化为内存数据
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	// 序列号数据
	cities := []string{"shanghai", "郑州", "shangqiu"}
	bs, err := json.Marshal(cities)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(bs)         // [91 34 115 104 97 110 103 104 97 105 34 44 34 233 131 145 229 183 158 34 44 34 115 104 97 110 103 113 105 117 34 93]
	fmt.Println(string(bs)) // ["shanghai","郑州","shangqiu"]

	// 反序列化数组
	var c2 []string
	err = json.Unmarshal(bs, &c2)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("%#v\n", c2) // []string{"shanghai", "郑州", "shangqiu"}

	// 序列化映射
	cityMap := map[string]string{"shanghai": "021", "zhengzhou": "0371", "shangqiu": "0370"}
	bs, err = json.Marshal(cityMap)
	fmt.Println(string(bs)) // {"shanghai":"021","shangqiu":"0370","zhengzhou":"0371"}

	// 反序列化映射
	var cm2 map[string]string
	err = json.Unmarshal(bs, &cm2)
	fmt.Printf("%#v\n", cm2) // map[string]string{"shanghai":"021", "shangqiu":"0370", "zhengzhou":"0371"}

	// 缩进输出映射
	bs, err = json.MarshalIndent(cityMap, "", "  ")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(string(bs))
	/*
		{
		  "shanghai": "021",
		  "shangqiu": "0370",
		  "zhengzhou": "0371"
		}
	*/

	// 是否可以转化验证
	fmt.Println(json.Valid([]byte("[]"))) // true
	fmt.Println(json.Valid([]byte("[{"))) // false
}

自定义类型的序列化和反序列化

package main

import (
	"encoding/json"
	"fmt"
)

const (
	Large  = iota // large
	Medium        // medium
	Small         // small
)

type Size int

func (s Size) MarshalText() ([]byte, error) {
	switch s {
	case Large:
		return []byte("large"), nil
	case Medium:
		return []byte("medium"), nil
	case Small:
		return []byte("small"), nil
	default:
		return []byte(""), nil
	}
}

func (s *Size) UnmarshalText(bs []byte) error {
	switch string(bs) {
	case "large":
		*s = Large
	case "medium":
		*s = Medium
	case "small":
		*s = Small
	default:
		return fmt.Errorf("unknown: %s", string(bs))
	}
	return nil
}

func main() {
	// 自定义类型的序列化和反序列化
	var size Size = Small
	bs, _ := json.Marshal(size)
	fmt.Println(string(bs)) // "small"

	var s2 Size
	err := json.Unmarshal(bs, &s2)
	if err != nil {
		fmt.Printf(err.Error())
		return
	}
	fmt.Println(s2) // 2

	// 数组
	sizes := []Size{Large, Small, Medium}
	bs, _ = json.Marshal(sizes)
	fmt.Println(string(bs)) // ["large","small","medium"]
	var s3 []Size
	err = json.Unmarshal(bs, &s3)
	if err != nil {
		fmt.Printf(err.Error())
		return
	}
	fmt.Println(s3) // [0 2 1]
}

结构体序列化和反序列化

说明:

  1. 结构体若需要进行序列化和反序列化,首字母必须是大写的(即属性是公开的)
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	FirstName  string
	LastName   string
	MiddleName string
	isBoy      bool
}

func main() {
	p := Person{
		FirstName:  "xie",
		LastName:   "xianbin",
		MiddleName: "",
		isBoy:      false,
	}
	bs, err := json.Marshal(p)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	// isBoy 是非公开的,不能被序列化
	fmt.Println(string(bs)) // {"FirstName":"xie","LastName":"xianbin","MiddleName":""}
}

// *Output*
// &{John Smith }
// {"first_name":"John","last_name":"Smith"}

格式json字符串,参考:Go Struct Tag 类型介绍

encoding/xml

对xml进行序列化和反序列化

Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数