反射是指一类应用,可以通过采用某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
接口与反射
变量包括类型和值两部分。类型包括静态类型和具体类型,静态类型是指在编码时就确定的类型,例如int string等,具体类型是指在运行时才能知道的类型。反射就是建立在类型之上,反射主要与Go语言的接口类型相关,只有接口类型才有反射。一个interface{}类型的变量包含了两个指针,一个指向值的类型(具体类型),一个指向实际的值(对应的value)。
反射原理
Go语言中reflect包提供了两种类型可以访问接口变量的内容,reflect.TypeOf()和reflect.ValueOf()。
示例:
package main
import (
"fmt"
"reflect"
)
func main() {
var money float32 = 88.88
fmt.Println("type is:", reflect.TypeOf(money))
fmt.Println("value is:", reflect.ValueOf(money))
}
输出:
type is: float32
value is: 88.88
未知原有类型
执行reflect.ValueOf()后,得到一个类型为reflect.Value的变量,可以通过这个变量自身的Interface()方法获得接口变量的真实内容。然后可以通过类型判断进行转换,转换为原有真实变量。但是很多情况下,可能不知道要转换的具体类型,这时候可以通过遍历探测Filed来获取类型,示例:
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
Score float32
}
func (stu Student) ReflectCall() {
fmt.Println(stu)
}
func GetReflectType(in interface{}) {
getType := reflect.TypeOf(in)
fmt.Println("type:", getType.Name())
getValue := reflect.ValueOf(in)
fmt.Println("All filed:", getValue)
for i := 0; i < getType.NumField(); i++ {
filed := getType.Field(i)
value := getValue.Field(i).Interface()
fmt.Println("filed name:", filed.Name, " filed type:", filed.Type, " filed value:", value)
}
for i := 0; i < getType.NumMethod(); i++ {
method := getType.Method(i)
fmt.Println("method name:", method.Name, "method type:", method.Type)
}
}
func main() {
stu := Student{
Name: "bai",
Age: 18,
Score: 99.99,
}
GetReflectType(stu)
}
输出:
type: Student
All filed: {bai 18 99.99}
filed name: Name filed type: string filed value: bai
filed name: Age filed type: int filed value: 18
filed name: Score filed type: float32 filed value: 99.99
method name: ReflectCall method type: func(main.Student)
在函数GetReflectType中先获取位置类型interface的reflect.Type,然后对NumFiled进行遍历。 再通过reflect.Type的Field获取其Filed。最后通过Filed的Interface()得到对应的value。获取所属方法的步骤类似。
通过reflect.Value设置实际变量的值
reflect.Value是通过reflect.ValueOf(interface)获得的,只有当参数interface是指针的时候,才可以通过reflect.Value修改实际变量interface的值,即如果要修改反射类型的对象就一定要保证其值是“可寻址的”。示例:
package main
import (
"fmt"
"reflect"
)
func main() {
var money float32 = 33.33
pointer := reflect.ValueOf(&money)
newValue := pointer.Elem()
fmt.Println("new Value type:", newValue.Type())
fmt.Println("new Value can Set:", newValue.CanSet())
if newValue.CanSet() {
newValue.SetFloat(99.99)
}
fmt.Println("new value:", money)
}
输出:
new Value type: float32
new Value can Set: true
new value: 99.99
通过reflect.ValueOf()函数来进行方法的调用
在工程应用中reflect有一个高级用法,就是通过reflect进行方法的调用。比如在做框架时,需要可以随意扩展方法,或者说用户可以自定义方法,那么通过什么手段来扩展让用户能够自定义,关键点在于用户的自定义方法是未知的,因此可以通过reflect包来实现,示例:
package main
import (
"fmt"
"reflect"
)
type Programmer struct {
Id int
Name string
Level int
}
func (p Programmer) CallFuncHasArgs(name string, age int) {
fmt.Println("CallFuncHasArgs name: ", name, ",age:", age, ",original Programmer Name:", p.Name)
}
func (p Programmer) CallFuncNoArgs() {
fmt.Println("CallFuncNoArgs,original Programmer Name:", p.Name)
}
func main() {
pro := Programmer{
Id: 1,
Name: "Tom",
Level: 5,
}
getValue := reflect.ValueOf(pro)
method := getValue.MethodByName("CallFuncHasArgs")
args := []reflect.Value{reflect.ValueOf("gogogo"), reflect.ValueOf(20)}
method.Call(args)
method = getValue.MethodByName("CallFuncNoArgs")
args = make([]reflect.Value, 0)
method.Call(args)
}
输出:
CallFuncHasArgs name: gogogo ,age: 20 ,original Programmer Name: Tom
CallFuncNoArgs,original Programmer Name: Tom