Skip to content

反射

  • 反射(Reflection)是一种能够在程序运行时动态检查、访问和修改变量类型与值的机制
  • 它允许程序在编译期未知类型信息的情况下,对变量进行操作,常用于处理 JSON 序列化、ORM 框架、依赖注入等场景。

核心概念

  • reflect.Type:表示变量的类型信息,例如 intstringstruct 等。
  • reflect.Value:表示变量的值信息,例如 10"hello"{Name: "张三", Age: 18} 等。

操作反射值

获取反射值的类型和值

go
func main() {
	x := 42
	t := reflect.TypeOf(x)  // 获取类型信息(int)
	v := reflect.ValueOf(x) // 获取值信息(42)

	fmt.Println("Type:", t)  // 输出:Type: int
	fmt.Println("Value:", v) // 输出:Value: 42
}

从反射对象还原为普通变量

go
func main() {
	x := 42
	v := reflect.ValueOf(x) // 获取值信息(42)

    // 还原为 interface{},再断言为 int
    val := v.Interface().(int)
    fmt.Println(val) // 输出:42
}

修改反射值

  • 要修改反射值,需满足两个条件:
    • 反射对象必须是可寻址的(即变量本身,而非副本)。
    • 变量必须是可导出的(如结构体字段首字母大写)。
go
func main() {
	x := 42
    // 传入指针,再通过 Elem() 解引用获取可修改的反射值
	v := reflect.ValueOf(&x).Elem()

    // 修改反射值为 100
    v.SetInt(100)
    fmt.Println(v.Interface().(int)) // 输出:100
}

常用方法

反射方法

方法描述
reflect.TypeOf()获取变量的类型信息(reflect.Type)
reflect.ValueOf()获取变量的值信息(reflect.Value)

reflect.Type 方法

方法描述
reflect.Type.Name()获取类型的名称(如 "int"、"string")
reflect.Type.Kind()获取类型的类别(如 reflect.Int、reflect.String)
reflect.Type.Size()返回该类型变量在内存中的字节大小
reflect.Type.Elem()返回其指向的元素类型(如 *int 的 Elem () 是 int)
reflect.Type.NumField()若类型是结构体,返回字段数量(仅对结构体有效)
reflect.Type.Field(i)若类型是结构体,返回第 i 个字段的信息(reflect.StructField)
reflect.Type.Field(i).Name若类型是结构体,返回第 i 个字段的名称(如 "Name"、"Age")
reflect.Type.Field(i).Tag若类型是结构体,返回第 i 个字段的标签(如 json:"name"
reflect.Type.NumMethod()若类型是结构体,返回方法数量(仅对结构体有效)
reflect.Type.Method(i)若类型是结构体,返回第 i 个方法的信息(reflect.Method)
reflect.Type.Field(i).Type若类型是结构体,返回第 i 个方法的类型(如 string、int)

reflect.Value 方法

方法描述
reflect.Value.Kind()返回值的类型
reflect.Value.Type()返回值对应的 reflect.Type 类型信息
reflect.Value.Interface()将反射值转换为 interface{} 类型,可通过类型断言还原为原始类型
reflect.Value.IsValid()判断反射值是否有效(如对 nil 指针调用 Elem() 会返回无效值)
reflect.Value.IsNil()判断值是否为 nil(仅对指针、切片、映射、通道、接口、函数类型有效)
reflect.Value.Elem()返回其指向的元素值(如 *int 的 Elem () 是 int 值)
reflect.Value.SetXxx()修改反射值;如 SetInt()、SetString()、SetFloat() 等,需匹配值类型
reflect.Value.NumMethod()返回值的方法数量
reflect.Value.Method(i int)返回第 i 个方法的 reflect.Value(可调用)
reflect.Value.Call()调用反射值的方法;如 Call() 方法,需传入 []reflect.Value{} 类型的参数

反射与结构体

  • 反射常用于处理结构体,可动态访问其字段和方法
go
type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func (u User) Hello() string {
	return "Hello, " + u.Name
}

func main() {
	u := User{Name: "Alice", Age: 30}
	t := reflect.TypeOf(u)
	v := reflect.ValueOf(u)

	// 1. 获取结构体字段信息
	fmt.Println("结构体名称:", t.Name()) // 输出:User
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		fmt.Printf("字段 %d:名称=%s,类型=%s,标签=%s\n",
			i, field.Name, field.Type, field.Tag.Get("json"))
	}

	// 2. 调用结构体方法
	method := v.MethodByName("Hello")
	if method.IsValid() {
		// 调用无参方法(参数为 []reflect.Value{})
		result := method.Call([]reflect.Value{})
		fmt.Println(result[0].String()) // 输出:Hello, Alice
	}
}

注意事项

  • 反射操作绕过了编译期类型检查,运行时开销比直接操作变量高,频繁使用可能影响性能。

  • 反射操作可能因类型不匹配导致 panic(如对 int 调用 SetString()),需通过 IsValid()、Kind() 等方法提前检查。

go
v := reflect.ValueOf(42)
if v.Kind() == reflect.Int { // 检查类型
    v.SetInt(100)
}