反射
反射(Reflection)是 Go 语言提供的一种机制,允许程序在运行时检查类型信息和操作值。
反射基础
reflect 包
go
import "reflect"Type 和 Value
反射的核心是两个类型:
reflect.Type: 表示类型信息reflect.Value: 表示值信息
go
var x float64 = 3.14
// 获取类型
t := reflect.TypeOf(x)
fmt.Println(t) // float64
// 获取值
v := reflect.ValueOf(x)
fmt.Println(v) // 3.14类型反射
获取类型信息
go
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
// 获取类型
t := reflect.TypeOf(p)
fmt.Println(t.Name()) // Person
fmt.Println(t.Kind()) // struct
fmt.Println(t.NumField()) // 2
// 遍历字段
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("字段: %s, 类型: %s\n", field.Name, field.Type)
}类型检查
go
var x interface{} = "hello"
t := reflect.TypeOf(x)
switch t.Kind() {
case reflect.String:
fmt.Println("是字符串")
case reflect.Int:
fmt.Println("是整数")
case reflect.Bool:
fmt.Println("是布尔值")
default:
fmt.Println("其他类型")
}值反射
获取和设置值
go
var x float64 = 3.14
v := reflect.ValueOf(x)
fmt.Println(v.Float()) // 3.14
// 注意:ValueOf 返回的是值的副本
// 要修改值,需要传递指针
var y float64 = 3.14
v := reflect.ValueOf(&y).Elem()
v.SetFloat(2.71)
fmt.Println(y) // 2.71检查值是否有效
go
var v reflect.Value
if v.IsValid() {
fmt.Println("值有效")
} else {
fmt.Println("值无效")
}
// 检查零值
if v.IsZero() {
fmt.Println("是零值")
}类型转换
go
var x float64 = 3.14
v := reflect.ValueOf(x)
// 转换为 interface{}
i := v.Interface()
fmt.Println(i) // 3.14
// 类型断言
if f, ok := i.(float64); ok {
fmt.Println(f)
}结构体反射
遍历结构体字段
go
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
p := Person{Name: "Alice", Age: 30}
v := reflect.ValueOf(p)
t := reflect.TypeOf(p)
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("字段: %s, 值: %v, 标签: %s\n",
field.Name, value.Interface(), field.Tag.Get("json"))
}修改结构体字段
go
p := &Person{Name: "Alice", Age: 30}
v := reflect.ValueOf(p).Elem()
// 修改字段
nameField := v.FieldByName("Name")
if nameField.IsValid() && nameField.CanSet() {
nameField.SetString("Bob")
}
fmt.Println(p.Name) // Bob调用方法
go
type Calculator struct {
result int
}
func (c *Calculator) Add(n int) {
c.result += n
}
func (c Calculator) GetResult() int {
return c.result
}
c := &Calculator{}
v := reflect.ValueOf(c)
// 调用方法
addMethod := v.MethodByName("Add")
addMethod.Call([]reflect.Value{reflect.ValueOf(10)})
resultMethod := v.MethodByName("GetResult")
results := resultMethod.Call(nil)
fmt.Println(results[0].Int()) // 10反射应用
JSON 编码器
go
func encodeJSON(v interface{}) (string, error) {
t := reflect.TypeOf(v)
vVal := reflect.ValueOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
vVal = vVal.Elem()
}
if t.Kind() != reflect.Struct {
return "", fmt.Errorf("只支持结构体")
}
result := "{"
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := vVal.Field(i)
jsonTag := field.Tag.Get("json")
if jsonTag == "" {
jsonTag = field.Name
}
if i > 0 {
result += ","
}
result += fmt.Sprintf(`"%s":%v`, jsonTag, value.Interface())
}
result += "}"
return result, nil
}函数调用包装
go
func callFunction(fn interface{}, args ...interface{}) []reflect.Value {
fnValue := reflect.ValueOf(fn)
fnType := fnValue.Type()
// 检查参数数量
if fnType.NumIn() != len(args) {
panic("参数数量不匹配")
}
// 转换参数
in := make([]reflect.Value, len(args))
for i, arg := range args {
in[i] = reflect.ValueOf(arg)
}
// 调用函数
return fnValue.Call(in)
}
// 使用
func add(a, b int) int {
return a + b
}
results := callFunction(add, 1, 2)
fmt.Println(results[0].Int()) // 3反射性能
反射比直接调用慢,应该谨慎使用:
go
// 直接调用(快)
result := add(1, 2)
// 反射调用(慢)
fnValue := reflect.ValueOf(add)
results := fnValue.Call([]reflect.Value{
reflect.ValueOf(1),
reflect.ValueOf(2),
})完整示例
go
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string `json:"name" validate:"required"`
Age int `json:"age" validate:"min=0,max=150"`
}
func (p Person) GetInfo() string {
return fmt.Sprintf("%s (%d 岁)", p.Name, p.Age)
}
func inspectStruct(v interface{}) {
t := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
val = val.Elem()
}
fmt.Printf("类型: %s\n", t.Name())
// 遍历字段
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := val.Field(i)
fmt.Printf(" 字段: %s, 类型: %s, 值: %v\n",
field.Name, field.Type, value.Interface())
// 读取标签
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
fmt.Printf(" JSON 标签: %s\n", jsonTag)
}
}
// 遍历方法
for i := 0; i < t.NumMethod(); i++ {
method := t.Method(i)
fmt.Printf(" 方法: %s\n", method.Name)
}
}
func main() {
p := Person{Name: "Alice", Age: 30}
inspectStruct(p)
// 调用方法
v := reflect.ValueOf(p)
method := v.MethodByName("GetInfo")
results := method.Call(nil)
fmt.Println("方法调用结果:", results[0].String())
}最佳实践
- 避免过度使用反射:反射会降低性能
- 缓存反射结果:如果频繁使用,缓存 Type 和 Value
- 检查有效性:使用 IsValid() 和 CanSet() 检查
- 处理错误:反射操作可能失败,要处理错误
- 类型安全:使用类型断言确保类型正确
下一步
接下来我们将学习:
