当前位置: 首页 > news >正文

Go-反射

概念

在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.2345fmt.Println("type: ", reflect.TypeOf(num))fmt.Println("value: ", reflect.ValueOf(num))// type:  float64// value:  1.2345}

针对reflect的两个接口返回值进一步探索

package mainimport ("fmt""reflect"
)type User struct {Id   intName stringAge  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()得到对应的valuefor 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 mainimport ("fmt""reflect"
)func main() {var num float64 = 1.2345fmt.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 mainimport ("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.Ptrfmt.Println(v.Kind())   // ptrv1 := reflect.ValueOf(s).Elem() // v.Kind() is reflect.Ptrfmt.Println(v1.Kind())          // struct
}

显然,v.Kind()的返回值是reflect.Ptr,而不是MyStruct类型。这时,我们可以使用reflect.Indirect()在反射中获取原始类型。

package mainimport ("fmt""reflect"
)type Person struct {name stringage  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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 2024最新最全面的Selenium 3.0 + Python自动化测试框架
  • LeetCode每日一题_572.另一棵树的子树
  • C#学习笔记14:SYN6288语音模块_Winform上位机控制软件
  • 使用Variadic Templates(可变参数模板)实现printf
  • electron 配置、打包 -报错解决
  • RocketMQ 的认证与授权机制
  • Hive自定义Serde,实现自定义多字符串作为分隔符
  • 【C++】对象模型和this指针
  • vivado ODT
  • 【HarmonyOS NEXT星河版开发学习】小型测试案例01-今日头条置顶练习
  • 【算法速刷(4/100)】LeetCode —— 155.最小栈
  • Java反序列化漏洞实战:原理剖析与复现步骤
  • 与大语言模型Transformer的奇妙旅程
  • 手机三要素接口怎么对接呢?(二)
  • MediaHub中的卡片实现进展汇报
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 【Linux系统编程】快速查找errno错误码信息
  • eclipse的离线汉化
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • es6--symbol
  • go语言学习初探(一)
  • hadoop集群管理系统搭建规划说明
  • Java 最常见的 200+ 面试题:面试必备
  • JAVA_NIO系列——Channel和Buffer详解
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Mocha测试初探
  • mongodb--安装和初步使用教程
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • Python实现BT种子转化为磁力链接【实战】
  • react 代码优化(一) ——事件处理
  • react-native 安卓真机环境搭建
  • 编写符合Python风格的对象
  • - 概述 - 《设计模式(极简c++版)》
  • 高程读书笔记 第六章 面向对象程序设计
  • 看域名解析域名安全对SEO的影响
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 用 Swift 编写面向协议的视图
  • 用mpvue开发微信小程序
  • hi-nginx-1.3.4编译安装
  • ​flutter 代码混淆
  • #{}和${}的区别是什么 -- java面试
  • #java学习笔记(面向对象)----(未完结)
  • #pragma预处理命令
  • #知识分享#笔记#学习方法
  • (12)Hive调优——count distinct去重优化
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (二)Eureka服务搭建,服务注册,服务发现
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (一)u-boot-nand.bin的下载
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)nsfocus-绿盟科技笔试题目
  • (转)甲方乙方——赵民谈找工作
  • .bat批处理(六):替换字符串中匹配的子串