Go-反射

  • Post author:
  • Post category:其他




反射

常用包而已:strings 、strconv、time、math、reflect、io、os、rutime


正常的开发,很少用到。因为效率低。


开发一些脚手架,自动实现一些底层的判断。

interface{} = any,由于这种动态类型是不确定的,我们可能在底层代码进行判断,从而选择使用什么来处理。



反射机制可以在程序运行的过程中,获取到信息(变量:类型、值。结构体:字段、方法)


我们可以通过反射来修改变量和值、通过反射来调用方法。


reflect 包

所有的方法几乎都在这里面


  • reflect.Type类型typeOf reflect .Value值valueof()
  • var age int = 18
  • var 变量名 变量类型 = 变量值
  • var user User = User{xx,xx}


类型:Type

(在编程中我们使用Type),指代的是某一种类型


种类:Kind

(反射更多时候用Kind来区分),指代的是相似的一些列类型

package main

import (
   "fmt"
   "reflect"
)

// 反射
/*
Type : reflect.TypeOf(a) , 获取变量的类型
Value :reflect.ValueOf(a) , 获取变量的值
*/

func main() {
   // 正常编程定义变量
   var a int = 3
   fmt.Println("type", reflect.TypeOf(a))
   fmt.Println("value", reflect.ValueOf(a))

   // 根据反射的值,来获取对象对应的类型和数值
   // 如果我们不知道这个对象的信息,我们可以通过这个对象拿到代码中的一切。
   // Value
   v := reflect.ValueOf(a) // string int User
   // Kind : 获取这个值的种类, 在反射中,所有数据类型判断都是使用种类。
   if v.Kind() == reflect.Float64 {
      fmt.Println(v.Float())
   }
   if v.Kind() == reflect.Int {
      fmt.Println(v.Int())
   }
   fmt.Println(v.Kind() == reflect.Float64)
   //fmt.Println(v.Type())

}

静态类型 & 动态类型

在反射过程中,编译的时候就知道变量类型的就是静态类型、如果在运行时候才知道类型的就是动态类型

静态类型:变量在声明时候给他赋予类型的

var name string // string是静态类型的
var age int  // int 是静态类型的

动态类型:在运行的时候可能发生变化,主要考虑赋值问题

var A interface{}   // interface{} 静态类型 

A = 10              // interface{} 静态类型   动态类型 int
A = "xuexiangban"   // interface{} 静态类型   动态类型 string


为什么要用反射:

1、我们需要编写一个函数,但是不知道函数传递给我的参数时什么?没约定好,传入的类型太多,这些类型不能统一表示,反射

2、我们在某些使用,需要根据条件来判断具体使用哪个函数处理问题,根据用户的输入来决定,这时候就需要对函数的参数进行反射,在运行期间来动态处理。


为什么不建议使用反射:

1、和反射相关的代码,不方便阅读,开发中,代码可读性(指标)很重要

2、Go语言是静态类型的语言,编译器可以找出开发时候的错误,如果代码中有大量反射代码,随时可能存在安全问题,panic,项目就终止

3、反射的性能很低,相对于正常的开发,至少慢2-3个数量级。项目关键位置低耗时,一定是不能使用反射的。更多时候使用约定。



反射获取变量信息


实现拿到一个对象,还原它的本身结构信息

reflect.TypeOf(v) : 获取变量的类型 Type

  • Type.kind , 找到种类
  • Type.NumField(),找到里面字段的数量

    • Type.Filed(i)
  • Type.NumMethod(),找到里面方法的数量

    • Type.Method(i)

reflect.ValueOf(v) : 获取变量的值 Value

  • Value.Field(i).Interface()
package main

import (
   "fmt"
   "reflect"
)

type User struct {
   Name string
   Age  int
   Sex  string
}

func (user User) Say(msg string) {
   fmt.Println("User 说:", msg)
}

// toString : 打印结构体信息
func (user User) PrintInfo() {
   fmt.Printf("姓名:%s,年龄:%d,性别:%s\n", user.Name, user.Age, user.Sex)
}

func main() {
   user := User{"kuangshen", 18, "男"}

   reflectGetInfo(user)
}

// 通过反射,获取变量的信息
func reflectGetInfo(v interface{}) {
   // 1、获取参数的类型Type , 可能是用户自己定义的,但是Kind一定是内部类型struct
   getType := reflect.TypeOf(v)
   fmt.Println(getType.Name()) // 类型信息 User
   fmt.Println(getType.Kind()) // 找到上级的种类Kind  struct

   // 2、获取值
   getValue := reflect.ValueOf(v)
   fmt.Println("获取到value", getValue)

   // 获取字段,通过Type扒出字段
   // Type.NumField() 获取这个类型中有几个字段  3
   // field(index) 得到字段的值
   for i := 0; i < getType.NumField(); i++ {
      field := getType.Field(i)              // 类型
      value := getValue.Field(i).Interface() // value
      // 打印
      fmt.Printf("字段名:%s,字段类型:%s,字段值:%v\n", field.Name, field.Type, value)
   }

   // 获取这个结构体的方法 , 获取方法的数量
   for i := 0; i < getType.NumMethod(); i++ {
      method := getType.Method(i)
      fmt.Printf("方法的名字:%s,方法类型:%s", method.Name, method.Type)
   }

}
/*
type User struct{
   Name string
   Age int
   Sex string
}
func (main.User) PrintInfo(){}
func (main.User) Say(string)
*/



反射修改变量的值

vaule.CanSet

vaule.SetXXX

package main

import (
   "fmt"
   "reflect"
)

// 反射设置变量的值
func main() {

   var num float64 = 3.14
   update(num)
   fmt.Println(num)

}
func update(v any) {
   // 通过反射修改值,需要操作对象的指针,拿到地址,然后拿到指针对象
   pointer := reflect.ValueOf(&v)
   newValue := pointer.Elem()

   fmt.Println("类型:", newValue.Type())
   fmt.Println("判断该类型是否可以修改:", newValue.CanSet())
   if newValue.Kind() == reflect.Float64 {
      // 通过反射对象给变量赋值
      newValue.SetFloat(2.21)
   }
   if newValue.Kind() == reflect.Int {
      // 通过反射对象给变量赋值
      newValue.SetFloat(2)
   }
}

修改结构体变量:通过属性名,来实现修改

func main() {
	user := User{"kuangshen", 18, "男"}

	value := reflect.ValueOf(&user)
	if value.Kind() == reflect.Ptr {
		// 获取指针对象
		newValue := value.Elem()
		if newValue.CanSet() {
			// 如果是结构体:1、需要找到对象的结构体字段名
			newValue.FieldByName("Name").SetString("狂神")
			newValue.FieldByName("Age").SetInt(26)
		}
	}

	fmt.Println(user)

	//reflectGetInfo(user)
}



反射调用方法

value.MethodByName(“PrintInfo”).Call(nil)

// 参数构建

args := make([]reflect.Value, 1)

args[0] = reflect.ValueOf(“这反射来调用的”)

通过反射调用user方法

user := User{"kuangshen", 18, "男"}

// 通过方法名,找到这个方法, 然后调用 Call() 方法来执行
// 反射调用方法
value := reflect.ValueOf(user)
fmt.Printf("kind:%s,type:%s\n", value.Kind(), value.Type())
value.MethodByName("PrintInfo").Call(nil) // 无参方法调用

// 有参方法调用
args := make([]reflect.Value, 1)
args[0] = reflect.ValueOf("这反射来调用的")
value.MethodByName("Say").Call(args) // 无参方法调用



反射调用函数

package main

import (
   "fmt"
   "reflect"
)

// 反射调用函数  func
func main() {
   // 通过函数名来进行反射
   // Kind func
   value1 := reflect.ValueOf(fun1)
   fmt.Println(value1.Kind(), value1.Type())
   value1.Call(nil)

   value2 := reflect.ValueOf(fun2)
   fmt.Println(value2.Kind(), value2.Type())

   args1 := make([]reflect.Value, 2)
   args1[0] = reflect.ValueOf(1)
   args1[1] = reflect.ValueOf("hahahhaha")
   value2.Call(args1)

   vuale3 := reflect.ValueOf(fun3)
   fmt.Println(vuale3.Kind(), vuale3.Type())
   args2 := make([]reflect.Value, 2)
   args2[0] = reflect.ValueOf(1)
   args2[1] = reflect.ValueOf("hahahhaha")
   resultValue := vuale3.Call(args2)
   fmt.Println("返回值:", resultValue)
}
func fun1() {
   fmt.Println("fun1:无参")
}
func fun2(i int, s string) {
   fmt.Println("fun2:有参 i=", i, " s=", s)
}
func fun3(i int, s string) string {
   fmt.Println("fun3:有参有返回值 i=", i, " s=", s)
   return s
}



版权声明:本文为qq_45371023原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。