首页 > 其他分享 >狂神说Go语言—Go反射

狂神说Go语言—Go反射

时间:2023-01-03 20:55:29浏览次数:30  
标签:反射 string fmt value reflect 类型 Go 狂神

什么是反射

反射可以再运行时动态的获取变量的信息,比如变量的类型、值等

如果是结构体,还可以获取到结构体本身的各种信息,比如结构体的字段和方法

通过反射,还可以修改变量的值、调用方法

使用反射,需要引入一个包: reflect,它定义了两个重要的类型Type和Value任意接口值在反射中都可以理解为由 reflect.Type和reflect.Value两部分组成,并且reflect包提供了reflect.TypeOf 和reflect.ValueOf两个函数来获取任意对象的Value和Type。

在使用反射时,需要首先理解类型(Type) 和种类(Kind)的区别。编程中,使用最多的是类型,但在反射中,当需要区分一个品种的类型时,就会用到种类(Kind) .

静态类型&动态类型

在反射的概念中,编译时就知道变量的是静态类型,运行时才知道一个变量类型叫做动态类型

静态类型:就是变量声明时赋予的类型

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

动态类型:在运行是可能改变,这主要依赖于它的赋值

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

为什么要用反射

需要反射的2个常见场景:
1.有时你需要编写一个函数,但是并不知道传给你的数类型是什么,可能是没约定好;也可能是传入的类型很多,这些类型并不能统一表示。这时反射就会用的上了。
⒉.有时候需要根据某些条件决定调用哪个函数,比如根据用户的输入来决定。这时就需要对函数和函数的参数进行反射,在运行期间动态地执行函数。
但是对于反射,还是有几点不太建议使用反射的理由:
1、与反射相关的代码,经常是难以阅读的。在软件工程中,代码可读性也是一个非常重要的指标。
2、Go语言作为一门静态语言,编码过程中,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接panic,可能会造成严重的后果。
3、反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关键位置的代码,尽量避免使用反射特性。

反射获取变量的信息

/*
获取字段
	1、先获取type对象,reflect.type
	Numfild() 获取有几个字段
	filed(index) 得到字段的值
	2、通过Filed获取没一个Filed字段
	3、interface(),得到对象的value

*/
package main

import (
   "fmt"
   "reflect"
)

type User struct {
   Name string
   Age  int
   Sex  string
}

func (use User) Say(msg string) {
   fmt.Println("User说:", msg)
}
func (user User) PrintInfo() {
   fmt.Printf("姓名:%s,年龄:%d,性别:%s\n", user.Name)
}

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

}
func reflectinfo(inter interface{}) {
   //获取参数类型
   gettype := reflect.TypeOf(inter)
   fmt.Println("get type is:", gettype.Name())
   fmt.Println("get kind is:", gettype.Kind())

   // 获取值
   getvalue := reflect.ValueOf(inter)
   fmt.Println("get value is:", getvalue)

   /*
      获取字段
         1、先获取type对象,reflect.type
         Numfild() 获取有几个字段
         filed(index) 得到字段的值
         2、通过Filed获取没一个Filed字段
         3、interface(),得到对象的value

   */
   for i := 0; i < gettype.NumField(); i++ {
      filed := gettype.Field(i)
      value := getvalue.Field(i).Interface()
      fmt.Printf("字段名:%s,字段类型:%s,字段值%v\n", filed.Name, filed.Type, value)
   }

   for i := 0; i < gettype.NumMethod(); i++ {
      method := gettype.Method(i)
      fmt.Printf("方法名:%s,方法类型%v\n", method.Name, method.Type)
   }
}

反射设置变量的值

package main

import (
   "fmt"
   "reflect"
)

type User2 struct {
   Name string
   Age  int
   Sex  string
}

func main() {
   use2 := User2{"kuangshen", 18, "男"}
   fmt.Println(use2)

   value := reflect.ValueOf(&use2)
   if value.Kind() == reflect.Ptr { //判断种类是否是指针
      newvalue := value.Elem()
      if newvalue.CanSet() { //判断是否能够被修改
         newvalue.FieldByName("Name").SetString("秦疆")
         newvalue.FieldByName("Age").SetInt(26)
      }
   }
   fmt.Println(use2)
}

反射调用的方法

package main

import (
   "fmt"
   "reflect"
)

type User3 struct {
   Name string
   Age  int
   Sex  string
}

func (user3 User3) Say3(msg string) {
   fmt.Println("User说:", msg)
}
func (user3 User3) PrintInfo3() {
   fmt.Printf("姓名:%s,年龄:%d,性别:%s\n", user3.Name, user3.Age, user3.Sex)
}
func main() {
   user3 := User3{"kuanshsen", 18, "男"}
   fmt.Println(user3)

   // 无参方法调用,可以传nil
   value := reflect.ValueOf(user3)
   value.MethodByName("printInfo").Call(nil)
   //有参方法调用,要先准备参数[]reflect.Value 切片
   args := make([]reflect.Value, 1)
   args[0] = reflect.ValueOf("反射来调用实现的")
   value.MethodByName("Say").Call(args)

}

标签:反射,string,fmt,value,reflect,类型,Go,狂神
From: https://www.cnblogs.com/DuPengBG/p/17023358.html

相关文章