什么是反射

Go语言中的反射是指在程序运行时检查程序的结构,比如变量的类型、方法的签名等。Go语言的反射包是reflect。通过反射,你可以动态地检查类型信息、获取字段和方法、调用方法等。

反射可以在运行时动态获取变量的各种信息,比如变量的类型、值等
如果是结构体,还可以获取到结构体本身的各种信息,比如结构体的字段、方法
通过反射,还可以修改变量的值、调用方法
使用反射,需要引入一个包:reflect,它定义了内个重要的类型 Type 和 Value 任意接口值在反射中都可以理解为由 reflect.Type 和 reflect.Value两部分组成,并且reflect 包提供了 reflect.Typ!alue和Type.Of和 reflect.ValueOf 两个函数来获取任意对象的 V
(Type)和种类(Kind)的区别。编程中,使用最多的是类型,但在反射中,当需要区分一个品种的类型时在使用反射时,需要首先理解类型.就会用到种类(Kind)。

package mainimport ("fmt""reflect")func main() {var a float64 = 3.14fmt.Println(reflect.TypeOf(a))fmt.Println(reflect.ValueOf(a))v := reflect.ValueOf(a)if v.Kind() == reflect.Float64 {fmt.Println("v.kind() is float64")}fmt.Println(v.Type())fmt.Println(v.Float())}

1.反射的应用场景

需要反射的2个常见场景:

  • 1.有时你需要编写一个函数,但是并不知道传给你的参数多型是什么,可能是没约定好;也可能是传入的类型很多,这时反射就会用的上了。
  • 2.有时候需要根据某些条件决定调用哪个函数,比如根据户的输入来决定。这时就需要对函数和函数的参数进行反函数。

但是对于反射,还是有几点不太建议使用反射的理由:

  • 1、与反射相关的代码,经常是难以阅读的。在软件工程中代码可读性也是一个非常重要的指标。
  • 2、Go语言作为一门静态语言,编码过程中,编译器能提发现一些类型错误,但是对于反射代码是无能为力的。所可能会运行很久,才会出错,这时候经常是直接panic,可会造成严重的后果。
  • 3、反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关钱位置的代码,尽量避免使用反射特性。

2. 反射获取变量信息

package mainimport ("fmt""reflect")type User struct {Name stringAgeintSexstring}func (user User) Say(msg string) {fmt.Println("user说", msg)}func main() {user := User{Name: "xiaolong", Age: 18, Sex: "男"}reflectGetInfo(user)}func reflectGetInfo(input interface{}) {//获取参数类型getType := reflect.TypeOf(input)fmt.Println("type is", getType.Name())fmt.Println("kind is", getType.Kind())//获取值getValue := reflect.ValueOf(input)fmt.Println("getValue is", getValue)//通过反射获取反射结果内的字段与方法for i := 0; i < getType.NumField(); i++ {filed := getType.Field(i)value := getValue.Field(i).Interface()fmt.Println(filed.Name, filed.Type, value)}for i := 0; i < getType.NumMethod(); i++ {method := getType.Method(i)fmt.Println(method.Name, method.Type)}}

3.反射修改对象的值

package mainimport ("fmt""reflect")func main() {var num float64 = 1.23point := reflect.ValueOf(&num) //想要通过指针修改值,需要传入指针newValue := point.Elem()fmt.Println(newValue.Kind())fmt.Println(newValue.Type())fmt.Println(newValue.CanSet())if newValue.CanSet() {newValue.SetFloat(3.14)}fmt.Println(num)}

修改结构体

package mainimport ("fmt""reflect")type User struct {Name stringAgeintSexstring}func (user User) Say(msg string) {fmt.Println("user说", msg)}func main() {user := User{Name: "xiaolong", Age: 18, Sex: "男"}fmt.Println(user)newValue := reflect.ValueOf(&user)if newValue.Kind() == reflect.Ptr { //首先是指针valueTemp := newValue.Elem()if valueTemp.CanSet() {valueTemp.FieldByName("Name").SetString("dalong")valueTemp.FieldByName("Age").SetInt(38)}}fmt.Println(user)}

4.反射调用方法函数

方法调用

package mainimport ("fmt""reflect")type User struct {Name stringAgeintSexstring}func (user User) Say(msg string) {fmt.Println("user说", msg)}func (user User) GetInfo() {fmt.Println("user 信息", user.Name, user.Age)}func main() {user := User{Name: "xiaolong", Age: 18, Sex: "男"}//reflectGetInfo(user)fmt.Println(user)//无参数方法调用value := reflect.ValueOf(user)value.MethodByName("GetInfo").Call(nil)//有参数方法调用args := make([]reflect.Value, 1)args[0] = reflect.ValueOf("反射调用")value.MethodByName("Say").Call(args)}

函数调用

package mainimport ("fmt""reflect")func main() {v1 := reflect.ValueOf(func1)v1.Call(nil)v2 := reflect.ValueOf(func2)args := make([]reflect.Value, 2)args[0] = reflect.ValueOf(1)args[1] = reflect.ValueOf("gagaga")v2.Call(args)v3 := reflect.ValueOf(func3)args1 := make([]reflect.Value, 2)args1[0] = reflect.ValueOf(1)args1[1] = reflect.ValueOf("gagaga")res := v3.Call(args1)fmt.Println(res[0].Interface())}func func1() {fmt.Println("无参数fun1")}func func2(i int, s string) {fmt.Println("有参数fun2", i, s)}func func3(i int, s string) string {fmt.Println("有参数fun3", i, s)return s}