反射是指在运行时动态的访问和修改任意类型对象的结构和成员,在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*
- reflect.Array
- Len() 获取长度
- Elem() 获取数组元素类型
- reflect.Slice
- reflect.Map
- Key() 获取映射键类型
- Elem() 获取映射值类型
- reflect.Ptr
- 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) 通过属性名获取属性
reflect.DeepEqual
对比 x 和 y 是否 深度相等
Value
reflect.Value
是一个结构体类型,用户获取变量值的信息,可以通过 reflect.ValueOf
函数获取某个变量值的信息
方法:
- 通用方法
- Type() 获取值类型
- CanAddr() 是否可以获取地址
- Addr() 获取地址
- CanInterface() 是否可以获取接口
- InterfaceData()
- Interface() 将变量转化为 interface{}
- CanSet() 是否可更新
- isValid() 是否初始化为零值
- Kind() 获取值类型枚举值
- NumMethod() 方法个数
- Method(int) 通过索引获取方法值
- MethodByName(string) 通过方法名称获取方法值
- Convert(Type) 转换为对应类型的值
- 修改方法
- 调用方法
- 特定类型方法
- reflect.Int*/reflect.Uint*
- Int() 获取 Int 对应类型值
- Uint() 获取 Uint 对应类型值
- reflect.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
- 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]
}
结构体序列化和反序列化
说明:
- 结构体若需要进行序列化和反序列化,首字母必须是大写的(即属性是公开的)
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进行序列化和反序列化