第六章 反射 v1.0
在go中,反射是一种强大的工具,可以在运行时检查和修改程序的结构和行为。通过反射,我们可以动态地操纵像变量、数据结构、方法和类型这样的对象的各种属性和行为。
一、reflect包
reflect包的核心是两个类型,分别是:Type(类型信息)和Value(值信息)
1、获取反射对象
go
// 接收一个接口值,返回其静态类型的Type对象
func TypeOf(i interface{}) Typego
// 接收一个接口值,返回其值的Value对象
func ValueOf(i interface{}) Valuego
package main
import (
"fmt"
"reflect"
)
func main() {
var x int = 42
t := reflect.TypeOf(x) // 获取类型信息
v := reflect.ValueOf(x) // 获取值信息
fmt.Println("Type:", t) // 输出:Type: int
fmt.Println("Value:", v) // 输出:Value: 42
}2、类型信息(Type)
reflect.Type提供了获取类型元数据的方法
go
// 返回类型的Kind
func Kind() Kind
// 返回自定义类型的名称;非自定义类型返回空字符串
func Name() string
// 返回结构体类型的字段数量;非结构体类型会panic
func NumField() int
// 返回结构体类型的索引为i的字段;索引范围为[0, NumField()),超出索引范围或非结构体类型会导致panic
func Field(i int) StructField
// 返回类型的方法数量
func NumMethod() int
// 返回类型的索引为i的方法;索引范围为[0, NumMethod()),超出索引范围会导致panic
func Method(int) Method
// 对指针、切片、映射等类型,返回其指向的元素类型
func Elem() Type3、值信息(Value)
reflect.Value提供了读取和修改原始值的方法
go
// 返回类型的Kind
func Kind() Kind
// 将反射对象转换回接口值,再通过类型断言获取原始值
func Interface() interface{}
// 对指针类型的Value,返回其指向的元素的Value
func Elem() Value
// 返回结构体类型的字段数量;非结构体类型会panic
func NumField() int
// 返回结构体类型的索引为i的字段;索引范围为[0, NumField()),超出索引范围或非结构体类型会导致panic
func Field(i int) Value
// 返回值是否可设置
func CanSet() boolgo
func Addr() Value
func Bool() bool
func Float() float64
func Int() int64go
// 修改值需满足可设置的条件
func Set(x Value)
func SetBool(x bool)
func SetFloat(x float64)
func SetInt(x int64)要通过reflect.Value修改原始值,必须满足可设置的条件:
- 反射对象由指针指向的变量创建(即
ValueOf(&x)) - 通过
Elem()获取指针指向的元素 - 被修改的字段/值必须是可导出的(结构体字段首字母大写)
go
func main() {
var x int = 10
v := reflect.ValueOf(&x) // 传入指针
// 检查是否可设置
fmt.Println("v.CanSet:", v.CanSet()) // false,此时v是指针,不可设置,需通过Elem()获取指向的值
elem := v.Elem() // 获取指针指向的元素(x)
fmt.Println("elem.CanSet:", elem.CanSet()) // true
elem.SetInt(20) // 修改值
fmt.Println("x =", x) // 输出:x = 20
}二、典型应用
go
func toJSON(v interface{}) string {
t := reflect.TypeOf(v)
val := reflect.ValueOf(v)
// 处理指针
if t.Kind() == reflect.Ptr {
t = t.Elem()
val = val.Elem()
}
// 非结构体返回默认值
if t.Kind() != reflect.Struct {
return "{}"
}
result := "{"
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fieldVal := val.Field(i)
// 读取json标签,默认用字段名
jsonKey := field.Tag.Get("json")
if jsonKey == "" {
jsonKey = field.Name
}
// 拼接键值对
switch fieldVal.Kind() {
case reflect.String:
result += fmt.Sprintf(`"%s":"%s"`, jsonKey, fieldVal.String())
case reflect.Int:
result += fmt.Sprintf(`"%s":%d`, jsonKey, fieldVal.Int())
}
if i < t.NumField()-1 {
result += ","
}
}
result += "}"
return result
}go
func validate(v interface{}) error {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return fmt.Errorf("not a struct")
}
t := val.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fieldVal := val.Field(i)
// 检查validate标签
if tag := field.Tag.Get("validate"); tag == "required" {
// 简单判断是否为空
if fieldVal.Kind() == reflect.String && fieldVal.String() == "" {
return fmt.Errorf("field %s is required", field.Name)
}
}
}
return nil
}
type User struct {
Name string `validate:"required"`
Age int
}
func main() {
u := User{Age: 20} // Name为空
if err := validate(&u); err != nil {
fmt.Println("Validation error:", err) // 输出:field Name is required
}
}