反射(Reflection)
什么是反射?
反射是程序在运行时检查变量类型和值的能力。Go 的反射通过 `reflect` 包提供,允许程序在运行时动态地获取类型信息、调用方法和修改值。
反射的基本概念
- Type:表示 Go 的类型,如 int、string、struct 等
- Value:表示 Go 的值,包含类型和实际值
- Kind:表示类型的种类,如 Int、String、Struct 等
reflect.Type 和 reflect.Value
package main
import (
"fmt"
"reflect"
)
func main() {
var x int = 42
// 获取类型和值
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
fmt.Println("类型:", t)
fmt.Println("值:", v)
fmt.Println("类型种类:", t.Kind())
fmt.Println("值的种类:", v.Kind())
}
基本类型反射
package main
import (
"fmt"
"reflect"
)
func inspect(value interface{}) {
v := reflect.ValueOf(value)
t := v.Type()
fmt.Printf("类型: %s, 种类: %s, 值: %v\n", t, t.Kind(), v)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fmt.Printf(" 整数值: %d\n", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fmt.Printf(" 无符号整数值: %d\n", v.Uint())
case reflect.Float32, reflect.Float64:
fmt.Printf(" 浮点数值: %f\n", v.Float())
case reflect.String:
fmt.Printf(" 字符串值: %s, 长度: %d\n", v.String(), v.Len())
case reflect.Bool:
fmt.Printf(" 布尔值: %v\n", v.Bool())
}
}
func main() {
inspect(42)
inspect(3.14)
inspect("Hello, Go!")
inspect(true)
inspect(uint(100))
}
结构体反射
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string `json:"name" db:"person_name"`
Age int `json:"age" db:"person_age"`
Email string `json:"email" db:"person_email"`
Address string `json:"address,omitempty"`
}
func printStructInfo(s interface{}) {
v := reflect.ValueOf(s)
t := v.Type()
fmt.Println("结构体类型:", t.Name())
fmt.Println("字段数量:", t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fieldValue := v.Field(i)
fmt.Printf(" 字段 %d:\n", i)
fmt.Printf(" 名称: %s\n", field.Name)
fmt.Printf(" 类型: %s\n", field.Type)
fmt.Printf(" 值: %v\n", fieldValue.Interface())
fmt.Printf(" 标签: %s\n", field.Tag)
}
}
func main() {
person := Person{
Name: "张三",
Age: 25,
Email: "zhangsan@example.com",
Address: "北京市",
}
printStructInfo(person)
}
修改值
package main
import (
"fmt"
"reflect"
)
func modifyValue(value interface{}) {
v := reflect.ValueOf(value)
// 检查是否可设置
if v.CanSet() {
switch v.Kind() {
case reflect.Int:
v.SetInt(100)
case reflect.String:
v.SetString("Modified")
case reflect.Bool:
v.SetBool(true)
}
}
}
func main() {
// 使用指针才能修改值
x := 42
modifyValue(&x)
fmt.Println("修改后:", x)
s := "Hello"
modifyValue(&s)
fmt.Println("修改后:", s)
}
通过指针修改结构体字段
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string
Age int
Email string
}
func setField(obj interface{}, fieldName string, newValue interface{}) {
v := reflect.ValueOf(obj)
// 解引用指针
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
// 获取字段
field := v.FieldByName(fieldName)
if field.IsValid() && field.CanSet() {
newVal := reflect.ValueOf(newValue)
if newVal.Type().AssignableTo(field.Type()) {
field.Set(newVal)
fmt.Printf("成功设置 %s = %v\n", fieldName, newValue)
} else {
fmt.Printf("类型不匹配: 无法将 %v 设置为 %s\n", newVal.Type(), field.Type())
}
} else {
fmt.Printf("字段 %s 不存在或不可设置\n", fieldName)
}
}
func main() {
user := User{
Name: "张三",
Age: 25,
Email: "zhangsan@example.com",
}
fmt.Println("修改前:", user)
setField(&user, "Name", "李四")
setField(&user, "Age", 30)
setField(&user, "Email", "lisi@example.com")
fmt.Println("修改后:", user)
}
调用方法
package main
import (
"fmt"
"reflect"
)
type Calculator struct {
result float64
}
func (c *Calculator) Add(a, b float64) float64 {
c.result = a + b
return c.result
}
func (c *Calculator) Subtract(a, b float64) float64 {
c.result = a - b
return c.result
}
func (c *Calculator) GetResult() float64 {
return c.result
}
func callMethod(obj interface{}, methodName string, args ...interface{}) {
v := reflect.ValueOf(obj)
// 解引用指针
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
method := v.MethodByName(methodName)
if method.IsValid() {
// 准备参数
in := make([]reflect.Value, len(args))
for i, arg := range args {
in[i] = reflect.ValueOf(arg)
}
// 调用方法
results := method.Call(in)
// 输出结果
for _, result := range results {
fmt.Printf("结果: %v\n", result.Interface())
}
} else {
fmt.Printf("方法 %s 不存在\n", methodName)
}
}
func main() {
calc := &Calculator{}
callMethod(calc, "Add", 10.0, 5.0)
callMethod(calc, "Subtract", 20.0, 8.0)
callMethod(calc, "GetResult")
}
切片和映射反射
package main
import (
"fmt"
"reflect"
)
func inspectSlice(s interface{}) {
v := reflect.ValueOf(s)
if v.Kind() == reflect.Slice {
fmt.Printf("切片长度: %d, 容量: %d\n", v.Len(), v.Cap())
fmt.Println("元素:")
for i := 0; i < v.Len(); i++ {
fmt.Printf(" [%d]: %v\n", i, v.Index(i).Interface())
}
}
}
func inspectMap(m interface{}) {
v := reflect.ValueOf(m)
if v.Kind() == reflect.Map {
fmt.Printf("Map 长度: %d\n", v.Len())
fmt.Println("键值对:")
for _, key := range v.MapKeys() {
value := v.MapIndex(key)
fmt.Printf(" %v: %v\n", key.Interface(), value.Interface())
}
}
}
func main() {
// 切片
numbers := []int{1, 2, 3, 4, 5}
inspectSlice(numbers)
// 映射
colors := map[string]string{
"red": "#FF0000",
"green": "#00FF00",
"blue": "#0000FF",
}
inspectMap(colors)
}
动态创建实例
package main
import (
"fmt"
"reflect"
)
type Product struct {
Name string
Price float64
}
func newInstance(typ reflect.Type) interface{
// 创建新实例
value := reflect.New(typ).Elem()
return value.Interface()
}
func main() {
// 通过类型创建实例
productType := reflect.TypeOf(Product{})
newProduct := newInstance(productType)
fmt.Printf("新实例类型: %T\n", newProduct)
fmt.Printf("新实例值: %+v\n", newProduct)
}
反射的实际应用:JSON 序列化
package main
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
type Student struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Active bool `json:"active"`
}
func toJSON(obj interface{}) string {
data, err := json.Marshal(obj)
if err != nil {
return ""
}
return string(data)
}
func fromJSON(jsonStr string, obj interface{}) {
err := json.Unmarshal([]byte(jsonStr), obj)
if err != nil {
fmt.Println("解析错误:", err)
}
}
func main() {
student := Student{
ID: 1,
Name: "张三",
Age: 20,
Active: true,
}
// 序列化为 JSON
jsonStr := toJSON(student)
fmt.Println("JSON:", jsonStr)
// 从 JSON 反序列化
var newStudent Student
fromJSON(jsonStr, &newStudent)
fmt.Printf("反序列化: %+v\n", newStudent)
}
反射的性能考虑
反射的性能开销
- 反射比直接调用慢很多
- 反射操作会绕过编译器的类型检查
- 反射代码更难调试和维护
最佳实践
- 只在必要时使用反射(如序列化、ORM、依赖注入等)
- 缓存反射结果以提高性能
- 考虑使用代码生成替代反射
- 在性能关键路径上避免使用反射
反射与接口
package main
import (
"fmt"
"reflect"
)
type Writer interface {
Write(data string) error
}
type File struct {
name string
}
func (f *File) Write(data string) error {
fmt.Printf("写入文件 %s: %s\n", f.name, data)
return nil
}
func checkInterface(obj interface{}) {
v := reflect.ValueOf(obj)
t := v.Type()
// 检查是否实现了接口
writerType := reflect.TypeOf((*Writer)(nil)).Elem()
if t.Implements(writerType) {
fmt.Println("实现了 Writer 接口")
// 调用接口方法
method := v.MethodByName("Write")
results := method.Call([]reflect.Value{reflect.ValueOf("Hello, Reflection!")})
fmt.Println("方法返回:", results[0].Interface())
} else {
fmt.Println("未实现 Writer 接口")
}
}
func main() {
file := &File{name: "test.txt"}
checkInterface(file)
var x int = 42
checkInterface(x)
}