概念
在Go语言中,反射(reflection)是指在运行时检查程序的结构、变量和接口的机制。可以通过反射获取和修改变量的值、获取变量的类型信息、调用方法等操作。
反射主要由reflect包提供,它定义了两个重要的类型:Type和Value。Type代表一个Go类型的元数据,Value则代表一个变量的值和类型信息。
通过反射,可以动态地获取变量类型信息和值,例如使用reflect.TypeOf()可以获取变量的类型,使用reflect.ValueOf()可以获取变量的值。还可以使用Value提供的方法获取和设置变量的值、调用方法等。
需要注意的是,反射操作相对较慢,使用反射会带来一定的性能损失。因此,应尽量避免过度使用反射,谨慎地选择使用反射机制。
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero
func ValueOf(i interface{}) Value {...}
翻译一下:ValueOf用来获取输入参数接口中的数据的值,如果接口为空则返回0
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {...}
翻译一下:TypeOf用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil
例子
执行reflect的两个接口
func main() {
var num float64 = 1.2345
fmt.Println("type: ", reflect.TypeOf(num))
fmt.Println("value: ", reflect.ValueOf(num))
// type: float64
// value: 1.2345
}
针对reflect的两个接口返回值进一步探索
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (u User) ReflectCallFunc() {
fmt.Println("Allen.Wu ReflectCallFunc")
}
func main() {
user := User{1, "Allen.Wu", 25}
DoFiledAndMethod(user)
}
// 通过接口来获取任意参数,然后一一揭晓
func DoFiledAndMethod(input interface{}) {
getType := reflect.TypeOf(input)
fmt.Println("get Type is :", getType.Name())
getValue := reflect.ValueOf(input)
fmt.Println("get all Fields is:", getValue)
// 获取方法字段
// 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
// 2. 再通过reflect.Type的Field获取其Field
// 3. 最后通过Field的Interface()得到对应的value
for i := 0; i < getType.NumField(); i++ {
field := getType.Field(i)
value := getValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}
// 获取方法
// 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
for i := 0; i < getType.NumMethod(); i++ {
m := getType.Method(i)
fmt.Printf("%s: %v\n", m.Name, m.Type)
}
}
反射设置值
package main
import (
"fmt"
"reflect"
)
func main() {
var num float64 = 1.2345
fmt.Println("old value of pointer:", num)
// 通过reflect.ValueOf获取num中的reflect.Value,注意,参数必须是指针才能修改其值
pointer := reflect.ValueOf(&num)
newValue := pointer.Elem()
fmt.Println("type of pointer:", newValue.Type())
fmt.Println("settability of pointer:", newValue.CanSet())
// 重新赋值
newValue.SetFloat(77)
fmt.Println("new value of pointer:", num)
// 如果reflect.ValueOf的参数不是指针,会如何?
pointer = reflect.ValueOf(num)
// newValue = pointer.Elem() // 如果非指针,这里直接panic,“panic: reflect: call of reflect.Value.Elem on float64 Value”
}
reflect.Indirect()函数
package main
import (
"fmt"
"reflect"
)
type MyStruct struct {
ID int
}
func (s *MyStruct) DoSomething() {
fmt.Println("Hello world")
}
func main() {
s := &MyStruct{ID: 123}
v := reflect.ValueOf(s) // v.Kind() is reflect.Ptr
fmt.Println(v.Kind()) // ptr
v1 := reflect.ValueOf(s).Elem() // v.Kind() is reflect.Ptr
fmt.Println(v1.Kind()) // struct
}
显然,v.Kind()的返回值是reflect.Ptr,而不是MyStruct类型。这时,我们可以使用reflect.Indirect()在反射中获取原始类型。
package main
import (
"fmt"
"reflect"
)
type Person struct {
name string
age int
}
func main() {
p1 := &Person{name: "Alice", age: 30}
v1 := reflect.ValueOf(p1)
fmt.Println("v1.Kind():", v1.Kind())
fmt.Println("v1.Type():", v1.Type())
fmt.Println("v1.CanSet():", v1.CanSet())
fmt.Println("v1.Elem().CanSet():", v1.Elem().CanSet())
v1 = reflect.Indirect(v1)
fmt.Println("v1.Kind():", v1.Kind())
fmt.Println("v1.Type():", v1.Type())
fmt.Println("v1.CanSet():", v1.CanSet())
//fmt.Println("v1.Elem().CanSet():", v1.Elem().CanSet())
}
输出
v1.Kind(): ptr
v1.Type(): *main.Person
v1.CanSet(): false
v1.Elem().CanSet(): true
v1.Kind(): struct
v1.Type(): main.Person
v1.CanSet(): true
// panic: reflect: call of reflect.Value.Elem on struct Value